Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file

version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/" # covers all .github/workflows/*
schedule:
interval: "weekly"
26 changes: 22 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,32 @@ on: [push, pull_request]
jobs:
Test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
malli: ["0.14", "0.15", "0.16", "0.17", "0.18", "0.19", "latest", "head"]
continue-on-error: ${{ matrix.malli == 'head' }}
steps:
- name: Check out repository code
uses: actions/checkout@v3
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Install clojure tools
uses: DeLaGuardo/setup-clojure@10.0
uses: DeLaGuardo/setup-clojure@4c7a6f613e5089821bb3bb2a33a3ee115578580d # 13.6.1
with:
cli: latest
- name: Run tests
run: clojure -T:build test
- name: Run tests (malli ${{ matrix.malli }})
run: |
case "${{ matrix.malli }}" in
latest)
clojure -X:test
;;
head)
sha=$(git ls-remote https://github.com/metosin/malli.git HEAD | cut -f1)
echo "Testing against metosin/malli@$sha"
clojure -Sdeps "{:aliases {:malli-head {:override-deps {metosin/malli {:git/url \"https://github.com/metosin/malli.git\" :git/sha \"$sha\"}}}}}" -X:test:malli-head
;;
*)
clojure -X:test:malli-${{ matrix.malli }}
;;
esac
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0

- name: Install clojure tools
uses: DeLaGuardo/setup-clojure@10.0
uses: DeLaGuardo/setup-clojure@4c7a6f613e5089821bb3bb2a33a3ee115578580d # 13.6.1
with:
cli: latest

Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,10 @@
/checkouts
/classes
/target

/.worktree.yml
/.claude
/bin/worktree-dev

/tmp/*
!/tmp/.keep
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 Gert Goet, ThinkCreate
Copyright (c) 2026 Gert Goet, ThinkCreate

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
77 changes: 73 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,85 @@ It's based on Rich Hickey's ideas from his talk ["Maybe Not"](https://youtu.be/Y

## Quickstart

[deps-try](https://github.com/eval/deps-try/blob/master/README.md#installation) has a built-in recipe that walks you through malli-select's features on the REPL ([recipe source](https://github.com/eval/deps-try/blob/master/recipes/malli/malli_select.clj)).
Run like so:
Try it out using [deps-try](https://github.com/eval/deps-try/blob/master/README.md#installation):

``` clojure
$ deps-try --recipe malli/malli-select
$ deps-try io.github.eval/malli-select metosin/malli

user=> (require '[malli-select.core :as ms])
user=> (def Person
[:map
[:name string?]
[:age pos-int?]
[:addresses [:vector [:map
[:street string?] [:zip string?]]]]])

;; require :name, everything else is optional
user=> (ms/select Person [:name])
[:map
[:name string?]
[:age {:optional true} pos-int?]
[:addresses
{:optional true}
[:vector
[:map
[:street {:optional true} string?]
[:country {:optional true} string?]]]]]

;; *if* any address is provided, it should at least have :street
user=> (ms/select Person [{:addresses [:street]}])
[:map
[:name {:optional true} string?]
[:age {:optional true} pos-int?]
[:addresses
{:optional true}
[:vector
[:map [:street string?] [:country {:optional true} string?]]]]]

;; example valid data:
;; {}, {:addresses []}, {:addresses [{:street "Main"}]}
;;
;; example invalid data:
;; {:addresses nil}, {:addresses [{}]}, {:addresses [{:street "Foo" :country :se}]}

;; any address provided should be a full address
user=> (ms/select Person [{:addresses ['*]}])
;;
;; require all attributes of a person (shallow, i.e. address attributes become optional)
user=> (ms/select Person ['*])
;; example valid data:
;; {:name "Foo" :age 18 :addresses [{}]}


;; remove any optional attribute
user=> (ms/select Person [{:addresses ['*]}] {:prune-optionals true})
;; or shorter:
user=> (ms/select Person ^:only [{:addresses ['*]}])
;; example valid data:
;; {:name :not-a-string}
;;
;; Typically you'd use this to generate only specific data:
user=> (require '[malli.generator :as mg])
user=> (mg/generate (ms/select Person ^:only [:name]))
{:name "sNeLdUI5KtPw"}

;; selecting something not contained in the schema:
user=> (ms/select Person [:a])
Execution error (AssertionError) at dk.thinkcreate.malli-select/select (malli_select.clj:175).
Assert failed: Selection contains unknown paths: ([:a])

Available:
([:addresses] [:age] [:name] [:addresses :street] [:addresses :zip])

(empty? invalid-selection-paths)
;; bypass this check:
user=> (ms/select Person [:a] {:verify-selection false})
```

See [the tests](./test/malli_select/core_test.clj) for more.


## LICENSE

Copyright (c) 2023 Gert Goet, ThinkCreate.
Copyright (c) 2026 Gert Goet, ThinkCreate.
Distributed under the MIT license. See [LICENSE](LICENSE).
87 changes: 87 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Releasing

Releases are fully automated: pushing a git tag triggers the
[release workflow](.github/workflows/release.yml), which tests, builds and
deploys `dk.thinkcreate/malli-select` to [Clojars](https://clojars.org/dk.thinkcreate/malli-select).

## TL;DR

Local tags must be *signed* (`-s`):

```sh
# snapshot release (publishes X.Y.Z-SNAPSHOT)
git tag -s vX.Y.Z-pre.1 -m "vX.Y.Z-pre.1"
git push origin vX.Y.Z-pre.1

# full release (publishes X.Y.Z)
git tag -s vX.Y.Z -m "vX.Y.Z"
git push origin vX.Y.Z
```

Watch the run with `gh run watch`.

Alternatively create a [release on GitHub](https://github.com/eval/malli-select/releases/new)
(or `gh release create vX.Y.Z`) and let it create the tag — mark it as a
prerelease when using a `-pre` tag. Tags created this way aren't locally
signed but are attributed to your GitHub account.

## How it works

The workflow runs on every push to `main` and on every tag:

```sh
clojure -T:build release :build/git-version $(printf '"%s"' $(git describe --tags)) :deploy/only-jar-version-type :full-and-snapshot
```

`release` (see [build.clj](build.clj)) chains `test` → `build` → `deploy`.
The output of `git describe --tags` determines the version and whether the
built jar is actually deployed:

| `git describe --tags` | jar version | deployed? |
|-----------------------------------|-------------------|-------------------------------|
| `v1.2.3` (exact release tag) | `1.2.3` | yes — full release |
| `v1.2.3-pre.1` (pre-tag, or any commit after one) | `1.2.3-SNAPSHOT` | yes — snapshot |
| `v1.2.3-5-gabc123` (commits after a release tag) | `1.2.3-5-gabc123` | no — build only |

Consequences:

- A full release requires an *exact* `vX.Y.Z` tag on the commit.
- After pushing a `vX.Y.Z-pre.N` tag, every subsequent push to `main`
re-publishes `X.Y.Z-SNAPSHOT` — until the next exact release tag.
- Ordinary pushes to `main` after a release tag act as a dry run:
tests run and the jar is built, but nothing is deployed.
- Pick pre-tag versions to match the *next* intended release, e.g. after
releasing `v0.7.0` the next pre-tag should be `v0.8.0-pre.1`.
- For a full release the POM's `<scm><tag>` is set to `vX.Y.Z`.

## Credentials

`deploy` authenticates with the `CLOJARS_USERNAME` and `CLOJARS_PASSWORD`
[repository secrets](https://github.com/eval/malli-select/settings/secrets/actions).

- `CLOJARS_USERNAME`: the Clojars account name.
- `CLOJARS_PASSWORD`: a Clojars [deploy token](https://clojars.org/tokens) —
*not* the account password. Prefer a token scoped to the `dk.thinkcreate`
group.

To rotate: create a new token on Clojars, then
`gh secret set CLOJARS_PASSWORD --repo eval/malli-select`, and delete the old
token. The cheapest end-to-end check of the credentials is publishing a
snapshot via a `-pre` tag (see TL;DR); the token's "last used" date on the
Clojars tokens page should update.

## Local release

The same can be done locally (e.g. when CI is down):

```sh
CLOJARS_USERNAME=... CLOJARS_PASSWORD=<deploy-token> \
clojure -T:build release :build/git-version $(printf '"%s"' $(git describe --tags)) :deploy/only-jar-version-type :full-and-snapshot
```

## Gotchas

- `git describe --tags` needs the full history: the checkout step uses
`fetch-depth: 0` for this — keep it when touching the workflow.
- Tests run against the `:test` alias; a test failure aborts the release
before anything is built or deployed.
33 changes: 20 additions & 13 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -1,28 +1,35 @@
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.11.1" :scope "provided"}
metosin/malli {:mvn/version "0.13.0" :scope "provided"}}
metosin/malli {:mvn/version "0.20.1" :scope "provided"}}
:aliases
{:dev {:extra-deps {metosin/malli {:mvn/version "0.8.9"}
criterium/criterium {:mvn/version "0.4.6"}
{:dev {:extra-deps {criterium/criterium {:mvn/version "0.4.6"}
com.clojure-goes-fast/clj-async-profiler {:mvn/version "1.0.5"}}}
:neil {:project {:name malli-select/malli-select}}

:malli-0.9
{:override-deps {metosin/malli {:mvn/version "0.9.2"}}}
:malli-0.13
{:override-deps {metosin/malli {:mvn/version "0.13.0"}}}

:malli-0.10
{:override-deps {metosin/malli {:mvn/version "0.10.4"}}}
:malli-0.14
{:override-deps {metosin/malli {:mvn/version "0.14.0"}}}

:malli-0.11
{:override-deps {metosin/malli {:mvn/version "0.11.0"}}}
:malli-0.15
{:override-deps {metosin/malli {:mvn/version "0.15.0"}}}

:malli-0.12
{:override-deps {metosin/malli {:mvn/version "0.12.0"}}}
:malli-0.16
{:override-deps {metosin/malli {:mvn/version "0.16.4"}}}

:malli-0.17
{:override-deps {metosin/malli {:mvn/version "0.17.0"}}}

:malli-0.18
{:override-deps {metosin/malli {:mvn/version "0.18.0"}}}

:malli-0.19
{:override-deps {metosin/malli {:mvn/version "0.19.2"}}}

:test ;; added by neil
{:extra-paths ["test"]
:extra-deps {metosin/malli {:mvn/version "0.8.9"}
io.github.cognitect-labs/test-runner {:git/tag "v0.5.1" :git/sha "dfb30dd"}}
:extra-deps {io.github.cognitect-labs/test-runner {:git/tag "v0.5.1" :git/sha "dfb30dd"}}
:exec-fn cognitect.test-runner.api/test}
:perf {#_#_:extra-paths ["perf"]
:extra-deps {criterium/criterium {:mvn/version "0.4.6"}
Expand Down
Empty file added tmp/.keep
Empty file.