General documentation / cheat sheets for various languages and services


Basic types

:id :first-name keywords evaluate to themselves. I.e., they're just keyword literals. They're not pointers or references to something else. To convert a string to a keyword: (keyword "name").
:js/alert ::port namespaced keywords are basically keywords, but it's more than just a convention faciliated by the double-colon syntax. A :: prefix, as with ::port, produces a keyword with the current namespace prepended to the given name. So after calling (ns server.core), ::port would evaluate to :server.core/port.

Keywords can only have one slash, but you can declare a keyword with a different namespace than the current one.

There's also an interesting effect that arises in destructuring. If you destructure a map with namespaced keys, the variable name you put into the scope is just the local (post-/) part of the keyword. E.g., (defn print-server-port [{:keys [server.core/port]}] (println port)).

split find-first zero? symbols represent other things, like functions or values. They can contain alphanumeric characters, *, +, !, -, _, ', and ?, but cannot start with a numeral.
"The end" "span { }" strings can contain literal newlines
\K \newline characters are unquoted escape sequences.
42 +42 -42 0.001 1e100 2e-16 numbers use decimal syntax.


Commas are optional; they are treated as whitespace.

'(user :name "Lester") '(1 8 19 47) '(a, alt, end) lists are usually immediately evaluated (i.e., (verb noun) would try to call verb with noun as the argument), but ' "quotes" the list, producing a list literal. (list x y) does the same thing as '(x y). Lists have singly-linked performance characteristics.
[user1, user2, admin1] [0 1 1 2 3 5] vectors are like lists, but don't invoke function application. Like list, you can use (vector 1 10) to create a vector like [1 10]. Vectors have random-access performance characteristics.
{:first "Chris" :last "Brown"} {:k "abbr", :v "cb"} maps are key-value pairs.
{"Chris" "Brown" "H"} #{101, 103, 107} sets are unordered collections of unique elements. Set literals cannot contain duplicates. Use set to create a set from other collections (and handle duplicates): (set [101 103 107 101]).

Collection operations

(cons x seq) Returns a new seq that prepends x to seq.
(map f coll) (map f & colls) Call f for each item in coll, and returns a new (lazy) sequence with each return value of calling f.

To evaluate strictly (non-lazily), use (mapv ...) (which has exactly the same signatures, but no transducer option) or wrap your in a (doall (map ...)).

The second form is rare, and is like zipping first and then running the resulting tuples through f. E.g.:

(map + [1 2 3] [10 20 30]) (11 22 33)

Using the multi-collection form, the zipper stops zipping at the end of the shortest coll in colls.

Omitting the collection(s) returns a transducer.

(conj coll x) (conj coll x & xs) Returns a new collection with the x (or x and xs) added.

The specific operation — prepend vs. append (or perhaps even insertion) — and thus the resulting order — depends on the type of collection (the "concrete type").

(conj [1 2] 3) [1 2 3] (conj '(1 2) 3) (3 1 2)

If coll is nil, it's treated as if it were () (the empty list).

(list* xs) (list* x & xs) Returns a new seq prepending all items but the last to the last item. Basically it's the same as (list ...), but the last argument gets splatted in at the end.
(into to from) conj's each of from onto to.

There is also a transducer form.

(concat) (concat x) (concat x y) (concat x y & zs) Returns a lazy seq combining x, y, and each item in zs, in order from left to right. x, y, and each item in zs must be "ISeqable".
(flatten xs) Recursively flattens xs into a single sequence. Returns the empty list if xs is not a sequence.

To non-recursively flatten a list: (apply concat xs)

Or, without even using concat: (mapcat identity xs)

(mapcat f & xs) Flat-maps f across xs. f should take a single argument x (from xs) and return a collection.

It's effectively the same (equivalent?) as (apply concat (map f xs)).

(filter f xs) Return a lazy sequence of each x from xs where (f x) returns true.

Omitting the collection ((filter f)) returns a transducer.

(remove f xs) remove is the opposite of filter. (remove f xs) is equivalent to (filter (complement f) xs).
(filterv f xs) Strict version of filter; returns a vector.

(Unlike filter, does not have a transducer overload.)

(Also, there is no removev.)

(keep f xs) Returns a lazy sequence of the results of calling (f x) on each x in xs, dropping nil values. (keep f xs) is equivalent to (remove nil? (map f xs)).
(doall coll) Forces a lazy sequence to evaluate, and returns the sequence.
(dorun coll) Forces a lazy sequence to evaluate, and returns nil.
(doseq seq-bindings & body) Like (for seq-bindings & body) but non-lazy and returns nil.

Basic syntax

Clojure syntax is minimal. These are the basic categories:

Category Examples
Comments ;
Numeric types 1, 2.3e4, 5N, 6.7M, 8/9
Character types "a", \b, \space
Other types nil, true, #".+", :a, :b/c, d, e/f
Collection types (), [], #{}, {}
Macros ', `, ~, [email protected]
Readers #?, #[email protected], etc.

The characters allowed in symbols and keywords are pretty flexible, but have some limitations. From the Clojure reference on The Reader:

In practice, the set of characters used in symbols is even wider:

(def name "Chris") variables are set at the top level in the current namespace.
(def ^{:private true} conn-string "pg:db:root:5ecr3t") (def ^:private conn-string "pg:db:root:5ecr3t") Variables can be made private to the module in which they are defined. The alternative syntax with lone keyword is sugar for the first. For ^:private specifically, this usage is idiomatic, but if there is more metadata than just :private, the full map syntax is generally used.
(def ^{:constant true} rand-seed 2016) Variables can be declared constant, though I'm not sure what the point is, what with Clojure being immutable and all. Maybe for JVM-compliance?
(fn [x] (* x x)) functions are anonymous, but can be set to a variable with def.
(defn square "Multiply the argument by itself" [x] (* x x)) (defn name args body) is sugar for (def name (fn name args body)), but also facilitates supplying a string describing the function's purpose.
(defn name doc-string? [params*] prepost-map? body*) (defn name doc-string? ([params*] prepost-map? body*)+) (fn name? [params*] prepost-map? body*) (fn name? ([params*] prepost-map? body*)+)
where prepost-map looks like:
{:pre [pre-expr*] :post [post-expr*]}
The prepost-map? part of the defn/fn syntax is an optional map that immediately follows the argument vector (aka. the "arglist", aka. [params*]).
  • The only allowed keys are :pre and :post, which can be used together (as depicted) or individually.
  • Their values are vectors of predicate forms, each of which are asserted before (:pre) calling the function, or after (:post) it returns.
  • In the :post conditions, the symbol % is bound to the returned value.
  • The assertions are never run if *assert* is (re-)bound to something falsey.
  • In the bizarre and probably mistaken case that you only supply a condition map and no subsequent forms, the map will be treated as the function body, and evaluated and returned as such. Because % will (probably) not be bound in that case, you'll probably get an error.
  • fn also supports this syntax, but specifying pre/post conditions is much less idiomatic in that case.
  • When defining a multi-arity function (with separate function bodies), each arity gets its own prepost-map, each directly following the argument vector. You can specify a prepost-map for one arity, but not the others.
Beyond the official docs on Special Forms, these pre/post conditions are rarely discussed, and even there only painfully briefly. But see (Michael) Fogus's blog post, Clojure's :pre and :post, which ranks surprisingly (considering it's from 2009 and quite short) high (1st) on searching Google for "clojure pre post".

Contrived example:

(defn delete-customer! "Find the customer identified by `id` and delete that row from the database, but then also return the deleted row. Using pre/post conditions, this function checks that `id` is a positive integer, and that the returned row is a map with an `:id` value identical to the given `id`. The function may very well throw other exceptions. E.g., if the database indicated by `db` doesn't exist." [db id] {:pre [(integer? id) (pos? id)] :post [(map? %) (= (get % :id) id)]} (let [rows (jdbc/query db ["SELECT * FROM customer WHERE id = ?" id])] (jdbc/delete! db :customer ["id = ?" id]) (first rows)))
(defn- connect [conf] (...)) Functions declared with defn- are private to the module in which they are defined.
(defn range-size ([from to] (- to from)) ([to] (range-size 0 from))) function overloading (a.k.a., "multiple arities") allows differentiating function behavior based on the number of arguments it's called with.
(defn join-name [first last & middles] (str first " " (join " " middles) " " last)) A function's variadic argument is signaled by & and comes last in the argument vector. The syntax (and implementation) is identical to destructuring a sequence.
(map #(str "(" % ")") ["c" "h" "b"]) ("(c)" "(h)" "(b)") The reader macro makes anonymous function syntax more concise. #(body) is kind of like shorthand for (fn [%1] (body)), but adapts the argument vector based on the number of %-prefixed names supplied in body.
% is short for %1, but the latter is preferred if your #(...) function takes more than one argument.
%& captures the variadic remainder, i.e., all the arguments beyond the highest %N present.
(let [x 100 y (* x 2)] (println (str "Starting at (" x ", " y ")")) [x, y]) Starting at (100, 200) [100 200] let takes a vector of symbol-expression pairs to declare immutable variables in local scope, then evaluates all the following expressions, returning the final expression.
(do (println (str "Starting at (0, 0)")) [0, 0]) Starting at (0, 0) [0 0] do is like let, but without the local variables.
(loop [x 0] ... (recur (inc x))) loop is a lot like let, but allows jumping back to just after binding the variables with recur. Otherwise, it will return the value of the last expression of whatever dead end it hits.
(for [x (range 4) y (range 2)] [x y]) ([0 0] [0 1] [1 0] [1 1] [2 0] [2 1] [3 0] [3 1]) for is like let, but each variable's expression must be a sequence, and it iterates through the cross product of all sequences.
The bindings vector supports three modifiers:
  • :let for nested bindings
  • :while for specifying an exit condition, which is like take-while (evaluated before each loop)
  • :when for skipping certain conditions, which is like filter

Namespaces and imports

As far as I can tell, dots in a namespace/package/module name are equivalent to alphanumerics except that they specify directory descent rather than file name, when mapping module names to filesystem location.

(ns Sets the current namespace to, which should usually be in the file PROJECT_ROOT/src/io/github/chbrown/docs.clj.
(ns ... (:require org.anist.render)) Can now call (org.anist.render/text "Hello")
(ns ... (:require [org.anist.render :as render])) Can now call (render/text "Hello")
(ns ... (:require [org.anist.render :refer [html pdf text]])) Can now call (text "Hello")
(ns ... (:require [org.anist.render :refer :all])) Wildcard import.
(ns ... (:import java.time.ZonedDateTime)) Import class from the new Java 8 time implementation. Can call it like (ZonedDateTime/now).
(ns ... (:import [java.time Duration Instant ZonedDateTime])) Import multiple classes from the java.time namespace.
(ns ... (:use ...)) :use is deprecated as of Clojure 1.4; there's nothing :use can do that :require + :refer (:refer was added in Clojure 1.4) can't do better.
(require 'org.anist.render) (import 'java.time.ZonedDateTime) In the REPL, you may want to require or import another module on the fly; this form takes a quoted "libspec" (see the full (ns ...) examples above).

Java interop

java.text.Normalizer$Form/NFKC Java allows declaring classes inside other classes, which are accessed statically, and slightly differently than classes nested in another layer of namespace. Instead of using . to access the inner class, use $.

java.text.Normalizer.normalize("Å", java.text.Normalizer.Form.NFC) in Scala/Java is equivalent to (java.text.Normalizer/normalize "Å" java.text.Normalizer$Form/NFC) in Clojure.

Functional building blocks

(apply f xs) (apply f x & xs) (apply f x y & xs) Call f with each x in xs as its arguments.

If multiple arguments are given, everything after f are combined as if with (list* ...), i.e., everything up until the last argument are prepended to the last argument.

Useful for splatting a list into multiple function arguments, since there is no function invokation syntax sugar like Python's stringify(' ', *separators).

Suppose you have a list of strings, (def my-strings '("Chris" "Brown")), that you want to join into a single string, with no separators. But (str x & ys) joins each of its arguments, not a list. (str my-strings) would simply return a string representation of the list.

(str my-strings) "(\"Chris\" \"Brown\")"

Instead, you'd want to use apply:

(apply str my-strings) "ChrisBrown"

(partial f & args) Curries f and returns a new function that will use each arg in args as an argument when eventually calling that new function.

Clojure is not generally lazy, so otherwise calling a function with an insufficient number of arguments will raise an arity error.

(comp) (comp f) (comp f g) (comp f g & fs) Compose multiple functions into a single function; the right-most function will be called first, then the next right-most, etc.

(apply (comp a b c) x) is equivalent to (a (b (c x)))

((comp f g h) init) can be simulated (reimplemented?) with reduce kind of like so:

(reduce (fn [args f] (apply f args)) init (reverse [f g h]))
(complement f) Return a new function that takes the same arguments as f, does the same thing as f, but returns the boolean opposite of what f returns. I.e., true if f returns false or nil, false otherwise.

(complement f) is equivalent to (comp not f).

(cond & clauses) Each clause is a pair of two functions; if the first returns true, the second gets called. The clauses are tested in order, and the (cond ...) itself returns the result of the second function in the first clause whose first function returns true. The last clause's test function can be :else, which always evaluates as if true (so it operates as an "otherwise" or default: in a switch construct.)

Threading operators

(-> x & forms) The thread-first operator.

Call each form in forms with x (or the value returned by the previous form) inserted as the second item in the form (the first argument to the function).

If a form takes only one argument, you may omit the surrounding parentheses.

(doto x & forms) Just like ->, except that all of the forms are just side-effects; the flow behaves as if each form in forms returned x.
(->> x & forms) The thread-last operator. (Also called the "thrush" operator?)

Call each form in forms with x (or the value returned by the previous form) appended as the last argument.

If a form takes only one argument, you may omit the surrounding parentheses.



Special syntax


(quote x)

Syntax quoting:

`(+ x y)



Unquote splicing:

[email protected]




(atom value) Creates a new Atom with the value value, and returns the atom.
(deref atom) @atom Returns the value of atom (as opposed to the Atom itself). Also known as "dereferencing", I'm guessing because (deref ...) was the original syntax, and the @ sugar came later.
(reset! atom value) Sets atom to value (discarding atom's current value), and returns value.
(swap! atom fn & args) Calls fn with the current value of atom (and args, if any) and sets the atom's value to the result (also, returns the new value of atom).

(def counter (atom 0)) @counter 0 (swap! counter inc) 1 (swap! counter inc) 2 @counter 2

Often you'll see an atom that's a map, where most changes to it consist of setting a single key to a new value.

(def person (atom {:first-name "Chris"})) (swap! person assoc :last-name "Brown") @person {:first-name "Chris", :last-name "Brown"}

(compare-and-set! atom oldval newval) Calls (reset! atom newval) iff (= @atom oldval). Returns the boolean value of (= @atom oldval).


Work in progress





(defrecord Template [id name html])

(def new-template (Template. "new" "hello-world" "Hello world!"))

(class new-template) user.Template

(ancestors new-template) nil

(ancestors (class new-template)) #{clojure.lang.IHashEq clojure.lang.Seqable clojure.lang.IKeywordLookup clojure.lang.ILookup clojure.lang.IObj clojure.lang.IMeta clojure.lang.IPersistentCollection java.lang.Object java.lang.Iterable java.util.Map clojure.lang.IRecord clojure.lang.Counted clojure.lang.Associative clojure.lang.IPersistentMap}


Ring is the de facto protocol for web requests and responses in Clojure.

Request maps have a standard set of fields:

Key Description
:server-port The port on which the request is being handled.
:server-name The resolved server name, or the server IP address.
:remote-addr The IP address of the client or the last proxy that sent the request.
:uri The request URI (the full path after the domain name).
:query-string The query string, if present.
:scheme The transport protocol, either :http or :https.
:request-method The HTTP request method, which is one of :get, :head, :options, :put, :post, or :delete.
:headers A Clojure map of lowercase header name strings to corresponding header value strings.
:body An InputStream for the request body, if present.

The clj-http library adds a few extensions:

Key Description
:body or String / / byte-array
:body-encoding character encoding to use when serializing :body (defaults to “UTF-8”)
:length Value to use (number of bytes) for the Content-Length: header if :body is an InputStream and thus has no predetermined length (defaults to -1, which implies Transfer-Encoding: chunked)
:accept What to use for the Accept: header
:headers A Clojure map of lowercase header name strings or keywords to corresponding header value strings or lists of strings.
:client-params Map of specific client parameters to be sent directly to the underlying Apache HttpComponents client
:insecure? True to accept untrusted SSL certificates (default is false)
:follow-redirects? False not to follow redirects automatically (default is true)
:max-redirects Maximum number of redirects to follow (set :throw-exceptions to true to throw an exception if this limit is exceeded)
:socket-timeout in milliseconds
:conn-timeout in milliseconds
:query-params query parameters as a map from strings / keywords to strings / keywords / numbers / nested maps (of the same structure)
:cookies cookies as map from strings to maps of cookie parameters, with keys like :discard, :path, :value, and :version
:form-params a map from strings / keywords (when :request-method is :put or :post)
:form-param-encoding character encoding to use when serializing form-params
:content-type what content type / serialization to perform on the :form-params value, e.g., :json (default is urlencoded); also supports :transit+json and :transit+msgpack with accompanying :transit-opts {:encode {:handlers ...} :decode {:handlers ...}} config
:multipart a vector of maps with keys like :name, :content, :part-name, :encoding, and :mime-type, where :content can be a String / / / byte-array

Response maps are simpler; they have three fields:



Utility functions that seem like they ought to be in the standard library.


(defn map-values
  "construct a new map with all the values of the given map passed through f"
  [f kvs]
  (into {} (for [[k v] kvs] [k (f v)])))

Alternatively, this can be written a bit more verbosely but perhaps more properly/Clojurely with reduce like so:

(defn map-values
  "construct a new map with all the values of the given map passed through f"
  [f kvs]
  (reduce (fn [out [k v]] (assoc out k (f v))) {} kvs))


The standard (update m k f) adds an entry to m with the key k and the value (f (get m k)), even if (get m k) is nil and even if f returns nil.

It’s perhaps more Clojureish to imagine that all maps contain every key with the value of nil, but sometimes it’s more practically useful to distinguish between key-value pairs with nil values and keys that are not contains?-ed by the map m.

(defn update-when
  "Like update but only calls f if m contains k"
  [m k f & args]
  (if (contains? m k) (apply update m k f args) m))


  ^{:doc "A lazier version of interleave that also does not truncate incomplete rows"
    :test (fn []
            (assert (= (interleave-all [1 3] [2]) [1 2 3]))
            (assert (= (interleave-all [1] [] [2 3]) [1 2 3])))}
  [& colls]
  ; filter is already lazy so no need to wrap the whole thing in lazy-seq
  (when-let [ss (seq (filter seq colls))]
    ; map is also lazy
    (concat (map first ss)
            ; but apply will cause (map rest ss) to be evaluated,
            ; so this lazy-seq is required
              (apply interleave-all (map rest ss))))))

export From clojure/core.clj (commented-out, but not private)

(defn export [syms]
  (doseq [sym syms]
    (.. *ns* (intern sym) (setExported true))))

dedupe-by Based on clojure.core/dedupe (also in the transducers reference)

(defn dedupe-by
  "Like clojure.core/dedupe, removes consecutive duplicates in coll,
  but compares values after passing elements from coll through f.
  Returns a transducer when no collection is provided."
   (fn [xf]
     (let [previous (volatile! ::none)]
         ([] (xf))
         ([result] (xf result))
         ([result input]
            (let [current (f input)
                  dupe? (= @previous current)]
              (vreset! previous current)
              (if dupe?
                (xf result input))))))))
  ([f coll] (sequence (dedupe-by f) coll)))

count-realized From; lazily counts the realized elements of a lazy sequence.

(defn count-realized
  "Count the realized portion of a sequence"
  (loop [s s
         n 0]
    (if (instance? clojure.lang.IPending s)
      (if (and (realized? s) (seq s))
        (recur (rest s) (inc n))
      (if (seq s)
        (recur (rest s) (inc n))

read-resource-version Uses to look up Maven metadata in the JVM classpath for the given package, which should be a symbol like 'aleph or 'org.clojure/clojure.

(defn- ^java.util.Properties ->Properties
  [^ reader]
  (doto (java.util.Properties.) (.load reader)))

(defn read-resource-version
  (let [package-name (name package)
        package-namespace (or (namespace package) (name package))]
    (some-> (str "META-INF/maven/" package-namespace "/" package-name "/")
            ; io/resource will return nil if given a path that doesn't exist
            ; Load the given reader as a Properties instance
            (.getProperty "version"))))

JavaScript ↔ ClojureScript translation pairs

JavaScript ClojureScript
let objContainsKey = key in obj (def objContainsKey (js-in key obj))


These packages are very useful development / testing helpers:

Their use is described below.


Test your entire project:

lein test

For nicer test failure explanations, add the dependency [pjstadig/humane-test-output "0.8.1"] and the following :injections to your ~/.lein/profiles.clj:

(require 'pjstadig.humane-test-output)

Then use the ordering (clojure.test/is (= expected actual)) for proper error messages.

To calculate test coverage; add [lein-cloverage "1.0.9"] to your list of plugins, then run:

lein cloverage

Code style

To list suggested changes in git diff format, add [lein-cljfmt "0.5.6"] to your plugins, and run:

lein cljfmt check

To make the changes in place, run:

lein cljfmt fix

To ‘lint’ your Clojure code, add the plugin, [jonase/eastwood "0.2.3"], and run:

lein eastwood

To enable more linters, specify them in an EDN string argument:

lein eastwood "{:add-linters [:keyword-typos :unused-namespaces :unused-private-vars :unused-fn-args :unused-locals]}"

To optimize/modernize your imports, add the dependency [slamhound "1.5.5"] and the alias {"slamhound" ["run" "-m" "slam.hound"]}, and then run:

lein slamhound src/


To list your project’s dependencies that are not the latest available version, add [lein-ancient "0.6.10"] to your plugins, and run:

lein ancient [upgrade [:interactive|:no-tests]] [:all] [:allow-qualified] [:check-clojure]
option effect
check List outdated artifacts (default if no sub-command is used)
upgrade Update project.clj in-place with current versions if lein test runs successfully
upgrade :no-tests Update with current versions without checking the tests
upgrade :interactive Confirm each change to project.clj at CLI before writing
:all Include both your (defproject ...)’s :dependencies and :plugins (default is to look only at :dependencies)
:plugins Check only your project’s :plugins
:allow-qualified Also consider qualified versions of artifacts as candidates for latest version

Examples of commonly used combinations:

Deploy / Release / Publish

Source: Leiningen DEPLOY doc.

Generally, Clojure packages for general consumption are published to Clojars.

Some of the release process requires signing some amount of code/commits/tags, and is generally much easier when gpg-agent is running and loaded with a default key.

I use my Keybase key, which is handy:

# cd /into/my/secret/keybase/dir
gpg --allow-secret-key-import --import keybase.private.key
gpg --import keybase.public.key

The current keys available to the gpg agent are listable:

gpg --list-secret-keys
gpg --list-keys # might also be handy

Your project.clj should contain the key-value :deploy-repositories [["releases" :clojars]].

The easiest all-in-one publishing solution is simply lein release, which handles tagging, signing, and uploading to Clojars.

If you have the lein-pprint plugin installed, you can show what happens in a release by calling:

lein pprint :release-tasks

It will output a list of the lein commands it will run when you call lein release:

# 1. stop immediately if there are any uncommitted outstanding changes
["vcs" "assert-committed"]
# 2. remove the snapshot or other suffix from the version
["change" "version" "leiningen.release/bump-version" "release"]
# 3. commit the change from #2
["vcs" "commit"]
# 4. create a signed git tag for the current version
["vcs" "tag"]
# 5. push to Clojars, pulling credentials from ~/.lein/profiles.clj or ~/.lein/credentials.clj.gpg
# 6. bump the version number, and add a -SNAPSHOT suffix
["change" "version" "leiningen.release/bump-version"]
# 7. commit
["vcs" "commit"]
# 8. push to remote
["vcs" "push"]

Unfortunately, there is no rollback mechanism if one of these fail, so you might need to do your own cleanup if it breaks halfway through.

If it only got up to the commit part, might need to rollback the version change:

git reset --hard HEAD^

If it created the tag, but failed to upload to Clojars (e.g., incorrect credentials), you might also need to delete the tag:

git tag -d 1.2.3  # or whatever tag it just added

By default, the post-release version bump just increments the patch level.

You can alternatively run lein release :minor or lein release :major to increment the minor/major level. I’m not sure exactly how the :minor (or whichever) argument gets directed to the right command – the change version ... one – but that’s where it gets picked up.

I’m not sure why you’re supposed to decide what the next patch level is going to be while cutting the current release, so it’s often handy to run some subset of the release sequence.

For example, to bump the minor version without publishing:

lein do vcs assert-committed, \
  change version leiningen.release/bump-version :minor, \
  vcs commit, vcs tag

Or to set a specific version, check, and deploy:

lein do vcs assert-committed, change version set '"1.0.0-rc1"'
# leiningen doesn't re-read / internally update the version; easier to just restart:
lein do vcs commit, vcs tag
# take this opportunity to double-check: git show; git tag
lein do vcs push, deploy