From 2de0297ef05fac8fbfb3e82bb693debf78dea091 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Thu, 4 Jun 2026 17:34:51 +0200 Subject: [PATCH 01/13] Use dispatch :help auto-help; migrate dep add/upgrade/search/versions (pin cli to SHA) --- deps.edn | 3 +- src/babashka/neil.clj | 112 +++++++++++++++++++----------------------- 2 files changed, 53 insertions(+), 62 deletions(-) diff --git a/deps.edn b/deps.edn index d797b7b..085efdc 100644 --- a/deps.edn +++ b/deps.edn @@ -3,7 +3,8 @@ babashka/process {:mvn/version "0.4.16"} rewrite-clj/rewrite-clj {:mvn/version "1.1.46"} borkdude/rewrite-edn {:mvn/version "0.4.8"} - org.babashka/cli {:mvn/version "0.8.58"} + org.babashka/cli {:git/url "https://github.com/babashka/cli" + :git/sha "6cf99436baa8b5954bf9b56be80e8f21d2ae13b9"} cheshire/cheshire {:mvn/version "5.11.0"} io.github.seancorfield/deps-new {:git/url "https://github.com/seancorfield/deps-new" :git/tag "v0.10.3" diff --git a/src/babashka/neil.clj b/src/babashka/neil.clj index 3970815..b6ab76c 100644 --- a/src/babashka/neil.clj +++ b/src/babashka/neil.clj @@ -481,24 +481,6 @@ chmod +x bin/kaocha (spit (:deps-file opts) s)))))) (defn dep-versions [{:keys [opts]}] - (when (or (:help opts) (:h opts)) - (println (str/trim " -Usage: neil dep versions LIB - -List available versions of a Clojure dependency. Only supports Clojars. - - $ neil dep versions http-kit/http-kit - :lib http-kit/http-kit :version 2.7.0-alpha1 - :lib http-kit/http-kit :version 2.7.0-SNAPSHOT - :lib http-kit/http-kit :version 2.6.0 - :lib http-kit/http-kit :version 2.6.0-RC1 - :lib http-kit/http-kit :version 2.6.0-alpha1 - :lib http-kit/http-kit :version 2.5.3 - :lib http-kit/http-kit :version 2.5.3-SNAPSHOT - :lib http-kit/http-kit :version 2.5.2 - :lib http-kit/http-kit :version 2.5.1 - :lib http-kit/http-kit :version 2.5.0")) - (System/exit 0)) (let [lib (:lib opts) lib (symbol lib) versions (or (seq (clojars-versions lib opts)) @@ -586,21 +568,17 @@ details on the search syntax."))) search-results))) (defn dep-search [{:keys [opts]}] - (let [{:keys [search-term]} opts] - (if (or (:help opts) - (not (string? search-term)) - (str/blank? search-term)) - (print-dep-search-help) - (let [search-results (->> [dep-search-maven - dep-search-clojars] - (map #(% search-term)) - (apply concat))] - (when (empty? search-results) (System/exit 1)) - (doseq [search-result search-results] - (prn :lib (symbol (:group_name search-result) - (:jar_name search-result)) - :version (:version search-result) - :description (:description search-result))))))) + (let [{:keys [search-term]} opts + search-results (->> [dep-search-maven + dep-search-clojars] + (map #(% search-term)) + (apply concat))] + (when (empty? search-results) (System/exit 1)) + (doseq [search-result search-results] + (prn :lib (symbol (:group_name search-result) + (:jar_name search-result)) + :version (:version search-result) + :description (:description search-result))))) (defn git-url->lib [git-url] (when git-url @@ -740,29 +718,6 @@ details on the search syntax."))) (not (:git/url current)) (assoc :omit-git-url true))})))))) (defn dep-upgrade [{:keys [opts]}] - (when (or (:h opts) (:help opts)) - (println "Usage: neil dep upgrade [lib] [options]") - (println "") - (println " neil dep upgrade [options] Upgrade all libraries") - (println " neil dep upgrade LIB [options] Upgrade a single library") - (println " neil dep upgrade --lib LIB [options] Upgrade a single library") - (println "") - (println "Options:") - (println "") - (println (cli/format-opts - {:spec spec - :order [:lib :dry-run :alias :no-aliases]})) - (println "") - (println (str/trim " -Examples: - - neil dep upgrade ; upgrade all deps. - neil dep upgrade --dry-run ; print deps that would be upgraded. - neil dep upgrade --alias lint ; update only deps for the `lint` alias. - neil dep upgrade :lib clj-kondo/clj-kondo ; update a single dep. -")) - (System/exit 0)) - (let [lib (some-> opts :lib symbol) alias (some-> opts :alias) deps-to-check (->> (opts->specified-deps opts) @@ -900,10 +855,43 @@ test {:cmds ["add" "kaocha"] :fn add-kaocha} {:cmds ["add" "nrepl"] :fn add-nrepl} {:cmds ["add"] :fn print-help} - {:cmds ["dep" "versions"] :fn dep-versions :args->opts [:lib]} - {:cmds ["dep" "add"] :fn dep-add :args->opts [:lib]} - {:cmds ["dep" "search"] :fn dep-search :args->opts [:search-term]} - {:cmds ["dep" "upgrade"] :fn dep-upgrade} + {:cmds ["dep" "versions"] :fn dep-versions :args->opts [:lib] + :doc "List available versions of a library (Clojars only)." + :require [:lib] + :epilog (str/trim " +Examples: + + $ neil dep versions http-kit/http-kit + :lib http-kit/http-kit :version 2.7.0 + :lib http-kit/http-kit :version 2.6.0 + ...")} + {:cmds ["dep" "add"] :fn dep-add :args->opts [:lib] + :doc "Add a dependency to deps.edn :deps." + :spec spec + :order [:lib :version :sha :latest-sha :tag :latest-tag :deps/root :as :alias :deps-file :help]} + {:cmds ["dep" "search"] :fn dep-search :args->opts [:search-term] + :doc "Search Clojars for a string in any attribute of an artifact." + :require [:search-term] + :epilog (str/trim " +Examples: + + $ neil dep search \"babashka.nrepl\" + :lib babashka/babashka.nrepl :version 0.0.6 + +Search for a fully ns-qualified library, all artifacts in a group +(\"group-id:babashka\"), or a term in a library's description. See +http://github.com/clojars/clojars-web/wiki/Search-Query-Syntax for the syntax.")} + {:cmds ["dep" "upgrade"] :fn dep-upgrade + :doc "Upgrade libs in deps.edn (all, or a single lib)." + :spec spec + :order [:lib :dry-run :alias :no-aliases :help] + :epilog (str/trim " +Examples: + + neil dep upgrade ; upgrade all deps. + neil dep upgrade --dry-run ; print deps that would be upgraded. + neil dep upgrade --alias lint ; update only deps for the `lint` alias. + neil dep upgrade :lib clj-kondo/clj-kondo ; update a single dep.")} {:cmds ["dep" "update"] :fn dep-upgrade} ;; supported as an alias {:cmds ["license" "list"] :fn license-search :args->opts [:search-term]} {:cmds ["license" "search"] :fn license-search :args->opts [:search-term]} @@ -951,7 +939,9 @@ test (print-help m)))}] *command-line-args* {:spec spec - :exec-args {:deps-file "deps.edn"}}) + :exec-args {:deps-file "deps.edn"} + :prog "neil" + :help true}) nil) (when (= *file* (System/getProperty "babashka.file")) From fa8ff79b1a6760ae8af862477129ecaa0d551617 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Thu, 4 Jun 2026 21:02:52 +0200 Subject: [PATCH 02/13] Migrate full CLI to auto-help: :doc/:epilog/:require/:inherit; remove print-help and per-command help --- src/babashka/neil.clj | 260 ++++++++++++++---------------------------- 1 file changed, 86 insertions(+), 174 deletions(-) diff --git a/src/babashka/neil.clj b/src/babashka/neil.clj index b6ab76c..26e93db 100644 --- a/src/babashka/neil.clj +++ b/src/babashka/neil.clj @@ -368,23 +368,13 @@ chmod +x bin/kaocha s (str (str/trim (str nodes)) "\n")] (spit (:deps-file opts) s))))))))) -(defn print-dep-add-help [] - (println "Usage: neil add dep [lib] [options]") - (println "Options:") - (println (cli/format-opts - {:spec spec - :order [:lib :version :sha :latest-sha :tag :latest-tag :deps/root :as :alias :deps-file]}))) - (defn log [& xs] (binding [*out* *err*] (apply prn xs))) (defn dep-add [{:keys [opts]}] - (if (or (:help opts) (:h opts) (not (:lib opts))) - (print-dep-add-help) - (do - (ensure-deps-file opts) - (let [edn-string (edn-string opts) + (ensure-deps-file opts) + (let [edn-string (edn-string opts) edn-nodes (edn-nodes edn-string) lib (:lib opts) lib (symbol lib) @@ -478,7 +468,7 @@ chmod +x bin/kaocha nodes) s (str (str/trim (str nodes)) "\n")] (when-not missing? - (spit (:deps-file opts) s)))))) + (spit (:deps-file opts) s)))) (defn dep-versions [{:keys [opts]}] (let [lib (:lib opts) @@ -492,36 +482,6 @@ chmod +x bin/kaocha (doseq [v versions] (println :lib lib :version v))))) -(defn print-dep-search-help [] - (println (str/trim " -Usage: neil dep search [lib] - -Search Clojars for a string in any attribute of an artifact: - - $ neil dep search \"babashka.nrepl\" - :lib babashka/babashka.nrepl :version 0.0.6 - -You can also search for the fully ns-qualified library: - - $ neil dep search \"babashka/babashka.nrepl\" - :lib babashka/babashka.nrepl :version 0.0.6 - -To search for all artifacts in a group: - - $ neil dep search \"group-id:babashka\" - :lib babashka/babashka :version 1.1.173 - :lib babashka/fs :version 0.2.15 - ... - -A search string can also be matched in a library's description: - - $ neil dep search \"test framework\" - -will return libraries with 'test framework' in their description. - -See http://github.com/clojars/clojars-web/wiki/Search-Query-Syntax for -details on the search syntax."))) - (defn dep-search-maven [search-term] (let [;; A fully-qualified lib (group/artifact) can't be passed to Solr ;; verbatim: the `/` is a special character (starts a regex) and makes @@ -748,57 +708,10 @@ details on the search syntax."))) (doseq [dep-upgrade upgrades] (do-dep-upgrade opts dep-upgrade)))) -(defn print-help [_] - (println (str/trim " -Usage: neil - -Most subcommands support the options: - --alias Override alias name. - --deps-file Override deps.edn file name. - -Subcommands: - -add - dep Alias for `neil dep add`. - test adds cognitect test runner to :test alias. - build adds tools.build build.clj file and :build alias. - kaocha adds kaocha test runner to :kaocha alias. - nrepl adds nrepl server to :nrepl alias. - -dep - add: Adds --lib, a fully qualified symbol, to deps.edn :deps. - Run `neil dep add --help` to see all options. +(declare dispatch-table) - search: Search Clojars for a string in any attribute of an artifact - Run `neil dep search --help` to see all options. - - upgrade: Upgrade libs in the deps.edn file. - Run `neil dep upgrade --help` to see all options. - - versions: List available versions of a library (Clojars libraries only) - Run `neil dep versions -h` to see all options. - - update: Alias for `upgrade`. - -license - list Lists commonly-used licenses available to be added to project. Takes an optional search string to filter results. - search Alias for `list` - add Writes license text to a file - Options: - --license The key of the license to use (e.g. epl-1.0, mit, unlicense). --license option name may be elided when license key is provided as first argument. - --file The file to write. Defaults to 'LICENSE'. - -new - Create a project using deps-new - Run `neil new --help` to see all options. - -version - Commands for managing the :version key in the deps.edn project config. - Run `neil version --help` to see all options. - -test - Run tests. Assumes `neil add test`. Run `neil test --help` to see all options. -"))) +(defn print-help [_] + (println (cli/format-command-help {:table dispatch-table :prog "neil"}))) ;; licenses (def licenses-api-url "https://api.github.com/licenses") @@ -847,32 +760,24 @@ test (defn neil-test [{:keys [opts]}] (neil-test/neil-test opts)) -(defn -main [& _args] - (cli/dispatch - [{:cmds ["add" "dep"] :fn dep-add :args->opts [:lib]} - {:cmds ["add" "test"] :fn add-cognitect-test-runner} - {:cmds ["add" "build"] :fn add-build} - {:cmds ["add" "kaocha"] :fn add-kaocha} - {:cmds ["add" "nrepl"] :fn add-nrepl} - {:cmds ["add"] :fn print-help} - {:cmds ["dep" "versions"] :fn dep-versions :args->opts [:lib] - :doc "List available versions of a library (Clojars only)." - :require [:lib] - :epilog (str/trim " -Examples: - - $ neil dep versions http-kit/http-kit - :lib http-kit/http-kit :version 2.7.0 - :lib http-kit/http-kit :version 2.6.0 - ...")} - {:cmds ["dep" "add"] :fn dep-add :args->opts [:lib] - :doc "Add a dependency to deps.edn :deps." - :spec spec - :order [:lib :version :sha :latest-sha :tag :latest-tag :deps/root :as :alias :deps-file :help]} - {:cmds ["dep" "search"] :fn dep-search :args->opts [:search-term] - :doc "Search Clojars for a string in any attribute of an artifact." - :require [:search-term] - :epilog (str/trim " +(def dispatch-table + [{:cmds ["add"] :doc "Add a dep, test runner, build or nREPL alias."} + {:cmds ["add" "dep"] :fn dep-add :args->opts [:lib] :doc "Alias for `dep add`."} + {:cmds ["add" "test"] :fn add-cognitect-test-runner :doc "Add the cognitect test runner to the :test alias."} + {:cmds ["add" "build"] :fn add-build :doc "Add a tools.build build.clj and :build alias."} + {:cmds ["add" "kaocha"] :fn add-kaocha :doc "Add the kaocha test runner to the :kaocha alias."} + {:cmds ["add" "nrepl"] :fn add-nrepl :doc "Add an nrepl server to the :nrepl alias."} + + {:cmds ["dep"] :doc "Manage dependencies."} + {:cmds ["dep" "add"] :fn dep-add :args->opts [:lib] + :doc "Add a dependency to deps.edn :deps." + :require [:lib] + :spec spec + :order [:lib :version :sha :latest-sha :tag :latest-tag :deps/root :as :alias :deps-file :help]} + {:cmds ["dep" "search"] :fn dep-search :args->opts [:search-term] + :doc "Search Clojars for a string in any attribute of an artifact." + :require [:search-term] + :epilog (str/trim " Examples: $ neil dep search \"babashka.nrepl\" @@ -881,67 +786,74 @@ Examples: Search for a fully ns-qualified library, all artifacts in a group (\"group-id:babashka\"), or a term in a library's description. See http://github.com/clojars/clojars-web/wiki/Search-Query-Syntax for the syntax.")} - {:cmds ["dep" "upgrade"] :fn dep-upgrade - :doc "Upgrade libs in deps.edn (all, or a single lib)." - :spec spec - :order [:lib :dry-run :alias :no-aliases :help] - :epilog (str/trim " + {:cmds ["dep" "versions"] :fn dep-versions :args->opts [:lib] + :doc "List available versions of a library (Clojars only)." + :require [:lib] + :epilog (str/trim " +Examples: + + $ neil dep versions http-kit/http-kit + :lib http-kit/http-kit :version 2.7.0 + :lib http-kit/http-kit :version 2.6.0 + ...")} + {:cmds ["dep" "upgrade"] :fn dep-upgrade + :doc "Upgrade libs in deps.edn (all, or a single lib)." + :spec spec + :order [:lib :dry-run :alias :no-aliases :help] + :epilog (str/trim " Examples: neil dep upgrade ; upgrade all deps. neil dep upgrade --dry-run ; print deps that would be upgraded. neil dep upgrade --alias lint ; update only deps for the `lint` alias. neil dep upgrade :lib clj-kondo/clj-kondo ; update a single dep.")} - {:cmds ["dep" "update"] :fn dep-upgrade} ;; supported as an alias - {:cmds ["license" "list"] :fn license-search :args->opts [:search-term]} - {:cmds ["license" "search"] :fn license-search :args->opts [:search-term]} - {:cmds ["license" "add"] :fn add-license :args->opts [:license]} - {:cmds ["new"] :fn new/run-deps-new - :args->opts [:template :name :target-dir] - :spec {:name {:coerce proj/coerce-project-name}}} - {:cmds ["version" "tag"] - :fn (partial neil-version/neil-version :tag) - :aliases {:h :help} - :spec neil-version/version-spec} - {:cmds ["version" "set"] - :fn (partial neil-version/neil-version :set) - :args->opts [:version] - :spec neil-version/version-spec - :aliases {:h :help}} - {:cmds ["version" "major"] - :fn (partial neil-version/neil-version :major) - :args->opts [:version] - :spec neil-version/version-spec - :aliases {:h :help}} - {:cmds ["version" "minor"] - :fn (partial neil-version/neil-version :minor) - :args->opts [:version] - :spec neil-version/version-spec - :aliases {:h :help}} - {:cmds ["version" "patch"] - :fn (partial neil-version/neil-version :patch) - :spec neil-version/version-spec - :args->opts [:version] - :aliases {:h :help}} - {:cmds ["version"] - :fn neil-version/neil-version - :aliases {:h :help} - :spec neil-version/version-spec} - {:cmds ["help"] :fn print-help} - {:cmds ["test"] :fn neil-test - :spec neil-test/neil-test-spec - :alias neil-test/neil-test-aliases} - {:cmds [] - :spec {:version {:coerce :boolean}} - :fn (fn [{:keys [opts] :as m}] - (if (:version opts) - (neil-version/print-version) - (print-help m)))}] - *command-line-args* - {:spec spec - :exec-args {:deps-file "deps.edn"} - :prog "neil" - :help true}) + {:cmds ["dep" "update"] :fn dep-upgrade :doc "Alias for `dep upgrade`."} + + {:cmds ["license"] :doc "Find and add a license."} + {:cmds ["license" "list"] :fn license-search :args->opts [:search-term] :doc "List commonly-used licenses (optional filter term)."} + {:cmds ["license" "search"] :fn license-search :args->opts [:search-term] :doc "Alias for `license list`."} + {:cmds ["license" "add"] :fn add-license :args->opts [:license] + :doc "Write a license file." + :require [:license] + :spec {:license {:ref "" :desc "License key (e.g. epl-1.0, mit, unlicense)."} + :file {:ref "" :desc "File to write. Defaults to LICENSE."}} + :order [:license :file :help]} + + {:cmds ["new"] :fn new/run-deps-new + :args->opts [:template :name :target-dir] + :doc "Create a project from a deps-new template." + :spec {:name {:coerce proj/coerce-project-name}}} + + {:cmds ["version"] :fn neil-version/neil-version + :doc "Show or manage the project version." + ;; declare version options once here, inherited by the subcommands below + :spec (reduce-kv (fn [m k v] (assoc m k (assoc v :inherit true))) + {} neil-version/version-spec)} + {:cmds ["version" "tag"] :fn (partial neil-version/neil-version :tag) :doc "Tag the current version in git."} + {:cmds ["version" "set"] :fn (partial neil-version/neil-version :set) :args->opts [:version] :doc "Set the project version."} + {:cmds ["version" "major"] :fn (partial neil-version/neil-version :major) :args->opts [:version] :doc "Bump the major version."} + {:cmds ["version" "minor"] :fn (partial neil-version/neil-version :minor) :args->opts [:version] :doc "Bump the minor version."} + {:cmds ["version" "patch"] :fn (partial neil-version/neil-version :patch) :args->opts [:version] :doc "Bump the patch version."} + + {:cmds ["test"] :fn neil-test :spec neil-test/neil-test-spec :alias neil-test/neil-test-aliases + :doc "Run tests (assumes `neil add test`)."} + + {:cmds ["help"] :fn print-help :doc "Show this help."} + {:cmds [] + :spec {:version {:coerce :boolean}} + :fn (fn [{:keys [opts] :as m}] + (if (:version opts) + (neil-version/print-version) + (print-help m))) + :epilog "Most subcommands also accept --alias and --deps-file. Run `neil --help` for a command's options."}]) + +(defn -main [& _args] + (cli/dispatch dispatch-table + *command-line-args* + {:spec spec + :exec-args {:deps-file "deps.edn"} + :prog "neil" + :help true}) nil) (when (= *file* (System/getProperty "babashka.file")) From e9ffd55ad9c4b1457bb58bff07fe43e3bbc0b3e8 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Thu, 4 Jun 2026 21:15:28 +0200 Subject: [PATCH 03/13] Remove dead :help branches from add-* commands (auto-help handles --help) --- src/babashka/neil.clj | 58 ++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/src/babashka/neil.clj b/src/babashka/neil.clj index 26e93db..6e83c10 100644 --- a/src/babashka/neil.clj +++ b/src/babashka/neil.clj @@ -203,47 +203,42 @@ (declare print-help) -(defn add-cognitect-test-runner [{:keys [opts] :as cmd}] - (if (:help opts) - (print-help cmd) - (do (add-alias opts :test cognitect-test-runner-alias) - (when-let [pn (proj/project-name opts)] - (let [test-ns (symbol (str (str/replace pn "/" ".") "-test")) - test-path (-> (str test-ns) - (str/replace "-" "_") - (str/replace "." fs/file-separator) - (str ".clj")) - test-path (fs/file "test" test-path)] - (when (or (not (fs/exists? "test")) - (zero? (count (fs/list-dir "test")))) - (fs/create-dirs (fs/parent test-path)) - (spit test-path - (format "(ns %s +(defn add-cognitect-test-runner [{:keys [opts]}] + (add-alias opts :test cognitect-test-runner-alias) + (when-let [pn (proj/project-name opts)] + (let [test-ns (symbol (str (str/replace pn "/" ".") "-test")) + test-path (-> (str test-ns) + (str/replace "-" "_") + (str/replace "." fs/file-separator) + (str ".clj")) + test-path (fs/file "test" test-path)] + (when (or (not (fs/exists? "test")) + (zero? (count (fs/list-dir "test")))) + (fs/create-dirs (fs/parent test-path)) + (spit test-path + (format "(ns %s (:require [clojure.test :as t :refer [deftest is testing]])) (deftest %s-test (testing \"TODO: fix\" (is (= :foo :bar)))) -" test-ns (name pn))))))))) +" test-ns (name pn))))))) (defn kaocha-alias-latest [] (let [version (latest-stable-clojars-version 'lambdaisland/kaocha)] {:extra-deps {'lambdaisland/kaocha {:mvn/version version}}, :main-opts ["-m" "kaocha.runner"]})) -(defn add-kaocha [{:keys [opts] :as cmd}] - (if (:help opts) - (print-help cmd) - (do - (add-alias opts :kaocha (kaocha-alias-latest)) - (println (str/trim " +(defn add-kaocha [{:keys [opts]}] + (add-alias opts :kaocha (kaocha-alias-latest)) + (println (str/trim " If you wish to create a `bin/kaocha` file, copy and run the following: mkdir -p bin && \\ echo '#!/usr/bin/env bash clojure -M:kaocha \"$@\"' > bin/kaocha && \\ chmod +x bin/kaocha -"))))) +"))) (defn nrepl-alias-latest [] (let [nrepl-version (future (latest-stable-clojars-version 'nrepl/nrepl)) @@ -255,10 +250,8 @@ chmod +x bin/kaocha :main-opts ["-m" "nrepl.cmdline" "--interactive" "--color" "--middleware" "[cider.nrepl/cider-middleware,refactor-nrepl.middleware/wrap-refactor]"]})) -(defn add-nrepl [{:keys [opts] :as cmd}] - (if (:help opts) - (print-help cmd) - (add-alias opts :nrepl (nrepl-alias-latest)))) +(defn add-nrepl [{:keys [opts]}] + (add-alias opts :nrepl (nrepl-alias-latest))) (defn build-alias-latest [] (let [latest-tag (git/latest-github-tag 'clojure/tools.build) @@ -336,11 +329,8 @@ chmod +x bin/kaocha "] base)) -(defn add-build [{:keys [opts] :as cmd}] - (if (:help opts) - (print-help cmd) - (do - (if-not (fs/exists? "build.clj") +(defn add-build [{:keys [opts]}] + (if-not (fs/exists? "build.clj") (spit "build.clj" (build-file opts)) (println "[neil] Project build.clj already exists.")) (ensure-deps-file opts) @@ -366,7 +356,7 @@ chmod +x bin/kaocha nodes (r/assoc-in nodes [:aliases :build :deps 'io.github.clojure/tools.build sha-key] (:sha ba)) s (str (str/trim (str nodes)) "\n")] - (spit (:deps-file opts) s))))))))) + (spit (:deps-file opts) s))))))) (defn log [& xs] (binding [*out* *err*] From 519f381bc4c6e2ddaa2e2bc0330868834747855e Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Thu, 4 Jun 2026 21:22:36 +0200 Subject: [PATCH 04/13] Pin cli to stable main SHA (e9536272) --- deps.edn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.edn b/deps.edn index 085efdc..ab3e493 100644 --- a/deps.edn +++ b/deps.edn @@ -4,7 +4,7 @@ rewrite-clj/rewrite-clj {:mvn/version "1.1.46"} borkdude/rewrite-edn {:mvn/version "0.4.8"} org.babashka/cli {:git/url "https://github.com/babashka/cli" - :git/sha "6cf99436baa8b5954bf9b56be80e8f21d2ae13b9"} + :git/sha "e9536272a28df41c7a2838839cc06fd0240eeac5"} cheshire/cheshire {:mvn/version "5.11.0"} io.github.seancorfield/deps-new {:git/url "https://github.com/seancorfield/deps-new" :git/tag "v0.10.3" From 8e7a4f40b4e18ee82b49fd846e3e2d762c6b95c7 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Thu, 4 Jun 2026 23:07:58 +0200 Subject: [PATCH 05/13] Bump cli to main SHA (de19235) --- deps.edn | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deps.edn b/deps.edn index ab3e493..0353b7c 100644 --- a/deps.edn +++ b/deps.edn @@ -4,12 +4,13 @@ rewrite-clj/rewrite-clj {:mvn/version "1.1.46"} borkdude/rewrite-edn {:mvn/version "0.4.8"} org.babashka/cli {:git/url "https://github.com/babashka/cli" - :git/sha "e9536272a28df41c7a2838839cc06fd0240eeac5"} + :git/sha "de19235af79da89c57696d5079a8a9899fd66229"} cheshire/cheshire {:mvn/version "5.11.0"} io.github.seancorfield/deps-new {:git/url "https://github.com/seancorfield/deps-new" :git/tag "v0.10.3" :git/sha "2c012c4"} - version-clj/version-clj {:mvn/version "2.0.2"}} + version-clj/version-clj {:mvn/version "2.0.2"} + clj-kondo/clj-kondo {:mvn/version "2026.05.25"}} :tools/usage {:ns-default babashka.neil.api} :aliases {:neil From 5834fd41dfc9c22afab5218667b109f1f41ca03b Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Thu, 4 Jun 2026 23:23:55 +0200 Subject: [PATCH 06/13] Fix CI for auto-cli migration --- CHANGELOG.md | 1 + README.md | 58 +--- neil | 383 +++++++++--------------- prelude | 6 +- src/babashka/neil.clj | 1 + test/babashka/neil/dep_upgrade_test.clj | 6 +- tests.clj | 13 +- 7 files changed, 168 insertions(+), 300 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1916e5..ad1f9c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ See the [New Clojure project quickstart](https://blog.michielborkent.nl/new-cloj ## 0.3.70 +- Auto-generated CLI help via babashka.cli - [#261](https://github.com/babashka/neil/issues/261): `neil dep upgrade` now upgrades unstable deps (e.g. release candidates) to a newer unstable version when no newer stable version exists - `neil dep search` for a fully-qualified lib (e.g. `org.clojure/clojurescript`) no longer fails: a `/` in the Solr query made central.sonatype.com return a 500 - [#258](https://github.com/babashka/neil/issues/258): `neil test` now exits with non-zero exit code when tests fail diff --git a/README.md b/README.md index f2e433b..e2cb5a5 100644 --- a/README.md +++ b/README.md @@ -59,54 +59,24 @@ version to version. Type `neil` to see the help: ``` -Usage: neil +Usage: neil [options] -Most subcommands support the options: - --alias Override alias name. - --deps-file Override deps.edn file name. +Commands: + add Add a dep, test runner, build or nREPL alias. + dep Manage dependencies. + license Find and add a license. + new Create a project from a deps-new template. + version Show or manage the project version. + test Run tests (assumes `neil add test`). + help Show this help. -Subcommands: +Options: + --version + -h, --help Show this help -add - dep Alias for `neil dep add`. - test adds cognitect test runner to :test alias. - build adds tools.build build.clj file and :build alias. - kaocha adds kaocha test runner to :kaocha alias. - nrepl adds nrepl server to :nrepl alias. +Most subcommands also accept --alias and --deps-file. Run `neil --help` for a command's options. -dep - add: Adds --lib, a fully qualified symbol, to deps.edn :deps. - Run `neil dep add --help` to see all options. - - search: Search Clojars for a string in any attribute of an artifact - Run `neil dep search --help` to see all options. - - upgrade: Upgrade libs in the deps.edn file. - Run `neil dep upgrade --help` to see all options. - - versions: List available versions of a library (Clojars libraries only) - Run `neil dep versions -h` to see all options. - - update: Alias for `upgrade`. - -license - list Lists commonly-used licenses available to be added to project. Takes an optional search string to filter results. - search Alias for `list` - add Writes license text to a file - Options: - --license The key of the license to use (e.g. epl-1.0, mit, unlicense). --license option name may be elided when license key is provided as first argument. - --file The file to write. Defaults to 'LICENSE'. - -new - Create a project using deps-new - Run `neil new --help` to see all options. - -version - Commands for managing the :version key in the deps.edn project config. - Run `neil version --help` to see all options. - -test - Run tests. Assumes `neil add test`. Run `neil test --help` to see all options. +Run "neil --help" for more information on a command. ``` ### add dep diff --git a/neil b/neil index 51fe8ef..9116f7d 100755 --- a/neil +++ b/neil @@ -11,10 +11,14 @@ io.github.seancorfield/deps-new {:git/url "https://github.com/seancorfield/deps-new" :git/tag "v0.10.3" :git/sha "2c012c4"} - org.babashka/cli {:mvn/version "0.8.58"} + org.babashka/cli {:git/url "https://github.com/babashka/cli" + :git/sha "de19235af79da89c57696d5079a8a9899fd66229"} version-clj/version-clj {:mvn/version "2.0.2"} org.babashka/http-client {:mvn/version "0.1.4"}}}) +;; force loading the add-deps cli over the bb built-in (needs format-command-help) +(require '[babashka.cli] :reload) + (ns babashka.neil.meta) (def version "This def was generated by the neil build script." "0.3.70") (ns babashka.neil.utils) @@ -1079,47 +1083,42 @@ using the [major|minor|patch] subcommands. (declare print-help) -(defn add-cognitect-test-runner [{:keys [opts] :as cmd}] - (if (:help opts) - (print-help cmd) - (do (add-alias opts :test cognitect-test-runner-alias) - (when-let [pn (proj/project-name opts)] - (let [test-ns (symbol (str (str/replace pn "/" ".") "-test")) - test-path (-> (str test-ns) - (str/replace "-" "_") - (str/replace "." fs/file-separator) - (str ".clj")) - test-path (fs/file "test" test-path)] - (when (or (not (fs/exists? "test")) - (zero? (count (fs/list-dir "test")))) - (fs/create-dirs (fs/parent test-path)) - (spit test-path - (format "(ns %s +(defn add-cognitect-test-runner [{:keys [opts]}] + (add-alias opts :test cognitect-test-runner-alias) + (when-let [pn (proj/project-name opts)] + (let [test-ns (symbol (str (str/replace pn "/" ".") "-test")) + test-path (-> (str test-ns) + (str/replace "-" "_") + (str/replace "." fs/file-separator) + (str ".clj")) + test-path (fs/file "test" test-path)] + (when (or (not (fs/exists? "test")) + (zero? (count (fs/list-dir "test")))) + (fs/create-dirs (fs/parent test-path)) + (spit test-path + (format "(ns %s (:require [clojure.test :as t :refer [deftest is testing]])) (deftest %s-test (testing \"TODO: fix\" (is (= :foo :bar)))) -" test-ns (name pn))))))))) +" test-ns (name pn))))))) (defn kaocha-alias-latest [] (let [version (latest-stable-clojars-version 'lambdaisland/kaocha)] {:extra-deps {'lambdaisland/kaocha {:mvn/version version}}, :main-opts ["-m" "kaocha.runner"]})) -(defn add-kaocha [{:keys [opts] :as cmd}] - (if (:help opts) - (print-help cmd) - (do - (add-alias opts :kaocha (kaocha-alias-latest)) - (println (str/trim " +(defn add-kaocha [{:keys [opts]}] + (add-alias opts :kaocha (kaocha-alias-latest)) + (println (str/trim " If you wish to create a `bin/kaocha` file, copy and run the following: mkdir -p bin && \\ echo '#!/usr/bin/env bash clojure -M:kaocha \"$@\"' > bin/kaocha && \\ chmod +x bin/kaocha -"))))) +"))) (defn nrepl-alias-latest [] (let [nrepl-version (future (latest-stable-clojars-version 'nrepl/nrepl)) @@ -1131,10 +1130,8 @@ chmod +x bin/kaocha :main-opts ["-m" "nrepl.cmdline" "--interactive" "--color" "--middleware" "[cider.nrepl/cider-middleware,refactor-nrepl.middleware/wrap-refactor]"]})) -(defn add-nrepl [{:keys [opts] :as cmd}] - (if (:help opts) - (print-help cmd) - (add-alias opts :nrepl (nrepl-alias-latest)))) +(defn add-nrepl [{:keys [opts]}] + (add-alias opts :nrepl (nrepl-alias-latest))) (defn build-alias-latest [] (let [latest-tag (git/latest-github-tag 'clojure/tools.build) @@ -1212,11 +1209,8 @@ chmod +x bin/kaocha "] base)) -(defn add-build [{:keys [opts] :as cmd}] - (if (:help opts) - (print-help cmd) - (do - (if-not (fs/exists? "build.clj") +(defn add-build [{:keys [opts]}] + (if-not (fs/exists? "build.clj") (spit "build.clj" (build-file opts)) (println "[neil] Project build.clj already exists.")) (ensure-deps-file opts) @@ -1242,25 +1236,15 @@ chmod +x bin/kaocha nodes (r/assoc-in nodes [:aliases :build :deps 'io.github.clojure/tools.build sha-key] (:sha ba)) s (str (str/trim (str nodes)) "\n")] - (spit (:deps-file opts) s))))))))) - -(defn print-dep-add-help [] - (println "Usage: neil add dep [lib] [options]") - (println "Options:") - (println (cli/format-opts - {:spec spec - :order [:lib :version :sha :latest-sha :tag :latest-tag :deps/root :as :alias :deps-file]}))) + (spit (:deps-file opts) s))))))) (defn log [& xs] (binding [*out* *err*] (apply prn xs))) (defn dep-add [{:keys [opts]}] - (if (or (:help opts) (:h opts) (not (:lib opts))) - (print-dep-add-help) - (do - (ensure-deps-file opts) - (let [edn-string (edn-string opts) + (ensure-deps-file opts) + (let [edn-string (edn-string opts) edn-nodes (edn-nodes edn-string) lib (:lib opts) lib (symbol lib) @@ -1354,27 +1338,9 @@ chmod +x bin/kaocha nodes) s (str (str/trim (str nodes)) "\n")] (when-not missing? - (spit (:deps-file opts) s)))))) + (spit (:deps-file opts) s)))) (defn dep-versions [{:keys [opts]}] - (when (or (:help opts) (:h opts)) - (println (str/trim " -Usage: neil dep versions LIB - -List available versions of a Clojure dependency. Only supports Clojars. - - $ neil dep versions http-kit/http-kit - :lib http-kit/http-kit :version 2.7.0-alpha1 - :lib http-kit/http-kit :version 2.7.0-SNAPSHOT - :lib http-kit/http-kit :version 2.6.0 - :lib http-kit/http-kit :version 2.6.0-RC1 - :lib http-kit/http-kit :version 2.6.0-alpha1 - :lib http-kit/http-kit :version 2.5.3 - :lib http-kit/http-kit :version 2.5.3-SNAPSHOT - :lib http-kit/http-kit :version 2.5.2 - :lib http-kit/http-kit :version 2.5.1 - :lib http-kit/http-kit :version 2.5.0")) - (System/exit 0)) (let [lib (:lib opts) lib (symbol lib) versions (or (seq (clojars-versions lib opts)) @@ -1386,36 +1352,6 @@ List available versions of a Clojure dependency. Only supports Clojars. (doseq [v versions] (println :lib lib :version v))))) -(defn print-dep-search-help [] - (println (str/trim " -Usage: neil dep search [lib] - -Search Clojars for a string in any attribute of an artifact: - - $ neil dep search \"babashka.nrepl\" - :lib babashka/babashka.nrepl :version 0.0.6 - -You can also search for the fully ns-qualified library: - - $ neil dep search \"babashka/babashka.nrepl\" - :lib babashka/babashka.nrepl :version 0.0.6 - -To search for all artifacts in a group: - - $ neil dep search \"group-id:babashka\" - :lib babashka/babashka :version 1.1.173 - :lib babashka/fs :version 0.2.15 - ... - -A search string can also be matched in a library's description: - - $ neil dep search \"test framework\" - -will return libraries with 'test framework' in their description. - -See http://github.com/clojars/clojars-web/wiki/Search-Query-Syntax for -details on the search syntax."))) - (defn dep-search-maven [search-term] (let [;; A fully-qualified lib (group/artifact) can't be passed to Solr ;; verbatim: the `/` is a special character (starts a regex) and makes @@ -1462,21 +1398,17 @@ details on the search syntax."))) search-results))) (defn dep-search [{:keys [opts]}] - (let [{:keys [search-term]} opts] - (if (or (:help opts) - (not (string? search-term)) - (str/blank? search-term)) - (print-dep-search-help) - (let [search-results (->> [dep-search-maven - dep-search-clojars] - (map #(% search-term)) - (apply concat))] - (when (empty? search-results) (System/exit 1)) - (doseq [search-result search-results] - (prn :lib (symbol (:group_name search-result) - (:jar_name search-result)) - :version (:version search-result) - :description (:description search-result))))))) + (let [{:keys [search-term]} opts + search-results (->> [dep-search-maven + dep-search-clojars] + (map #(% search-term)) + (apply concat))] + (when (empty? search-results) (System/exit 1)) + (doseq [search-result search-results] + (prn :lib (symbol (:group_name search-result) + (:jar_name search-result)) + :version (:version search-result) + :description (:description search-result))))) (defn git-url->lib [git-url] (when git-url @@ -1616,29 +1548,6 @@ details on the search syntax."))) (not (:git/url current)) (assoc :omit-git-url true))})))))) (defn dep-upgrade [{:keys [opts]}] - (when (or (:h opts) (:help opts)) - (println "Usage: neil dep upgrade [lib] [options]") - (println "") - (println " neil dep upgrade [options] Upgrade all libraries") - (println " neil dep upgrade LIB [options] Upgrade a single library") - (println " neil dep upgrade --lib LIB [options] Upgrade a single library") - (println "") - (println "Options:") - (println "") - (println (cli/format-opts - {:spec spec - :order [:lib :dry-run :alias :no-aliases]})) - (println "") - (println (str/trim " -Examples: - - neil dep upgrade ; upgrade all deps. - neil dep upgrade --dry-run ; print deps that would be upgraded. - neil dep upgrade --alias lint ; update only deps for the `lint` alias. - neil dep upgrade :lib clj-kondo/clj-kondo ; update a single dep. -")) - (System/exit 0)) - (let [lib (some-> opts :lib symbol) alias (some-> opts :alias) deps-to-check (->> (opts->specified-deps opts) @@ -1669,57 +1578,10 @@ Examples: (doseq [dep-upgrade upgrades] (do-dep-upgrade opts dep-upgrade)))) -(defn print-help [_] - (println (str/trim " -Usage: neil - -Most subcommands support the options: - --alias Override alias name. - --deps-file Override deps.edn file name. - -Subcommands: - -add - dep Alias for `neil dep add`. - test adds cognitect test runner to :test alias. - build adds tools.build build.clj file and :build alias. - kaocha adds kaocha test runner to :kaocha alias. - nrepl adds nrepl server to :nrepl alias. - -dep - add: Adds --lib, a fully qualified symbol, to deps.edn :deps. - Run `neil dep add --help` to see all options. +(declare dispatch-table) - search: Search Clojars for a string in any attribute of an artifact - Run `neil dep search --help` to see all options. - - upgrade: Upgrade libs in the deps.edn file. - Run `neil dep upgrade --help` to see all options. - - versions: List available versions of a library (Clojars libraries only) - Run `neil dep versions -h` to see all options. - - update: Alias for `upgrade`. - -license - list Lists commonly-used licenses available to be added to project. Takes an optional search string to filter results. - search Alias for `list` - add Writes license text to a file - Options: - --license The key of the license to use (e.g. epl-1.0, mit, unlicense). --license option name may be elided when license key is provided as first argument. - --file The file to write. Defaults to 'LICENSE'. - -new - Create a project using deps-new - Run `neil new --help` to see all options. - -version - Commands for managing the :version key in the deps.edn project config. - Run `neil version --help` to see all options. - -test - Run tests. Assumes `neil add test`. Run `neil test --help` to see all options. -"))) +(defn print-help [_] + (println (cli/format-command-help {:table dispatch-table :prog "neil"}))) ;; licenses (def licenses-api-url "https://api.github.com/licenses") @@ -1768,66 +1630,101 @@ test (defn neil-test [{:keys [opts]}] (neil-test/neil-test opts)) +(def dispatch-table + [{:cmds ["add"] :doc "Add a dep, test runner, build or nREPL alias."} + {:cmds ["add" "dep"] :fn dep-add :args->opts [:lib] :doc "Alias for `dep add`."} + {:cmds ["add" "test"] :fn add-cognitect-test-runner :doc "Add the cognitect test runner to the :test alias."} + {:cmds ["add" "build"] :fn add-build :doc "Add a tools.build build.clj and :build alias."} + {:cmds ["add" "kaocha"] :fn add-kaocha :doc "Add the kaocha test runner to the :kaocha alias."} + {:cmds ["add" "nrepl"] :fn add-nrepl :doc "Add an nrepl server to the :nrepl alias."} + + {:cmds ["dep"] :doc "Manage dependencies."} + {:cmds ["dep" "add"] :fn dep-add :args->opts [:lib] + :doc "Add a dependency to deps.edn :deps." + :require [:lib] + :spec spec + :order [:lib :version :sha :latest-sha :tag :latest-tag :deps/root :as :alias :deps-file :help]} + {:cmds ["dep" "search"] :fn dep-search :args->opts [:search-term] + :doc "Search Clojars for a string in any attribute of an artifact." + :require [:search-term] + :spec {:search-term {:coerce :string}} + :epilog (str/trim " +Examples: + + $ neil dep search \"babashka.nrepl\" + :lib babashka/babashka.nrepl :version 0.0.6 + +Search for a fully ns-qualified library, all artifacts in a group +(\"group-id:babashka\"), or a term in a library's description. See +http://github.com/clojars/clojars-web/wiki/Search-Query-Syntax for the syntax.")} + {:cmds ["dep" "versions"] :fn dep-versions :args->opts [:lib] + :doc "List available versions of a library (Clojars only)." + :require [:lib] + :epilog (str/trim " +Examples: + + $ neil dep versions http-kit/http-kit + :lib http-kit/http-kit :version 2.7.0 + :lib http-kit/http-kit :version 2.6.0 + ...")} + {:cmds ["dep" "upgrade"] :fn dep-upgrade + :doc "Upgrade libs in deps.edn (all, or a single lib)." + :spec spec + :order [:lib :dry-run :alias :no-aliases :help] + :epilog (str/trim " +Examples: + + neil dep upgrade ; upgrade all deps. + neil dep upgrade --dry-run ; print deps that would be upgraded. + neil dep upgrade --alias lint ; update only deps for the `lint` alias. + neil dep upgrade :lib clj-kondo/clj-kondo ; update a single dep.")} + {:cmds ["dep" "update"] :fn dep-upgrade :doc "Alias for `dep upgrade`."} + + {:cmds ["license"] :doc "Find and add a license."} + {:cmds ["license" "list"] :fn license-search :args->opts [:search-term] :doc "List commonly-used licenses (optional filter term)."} + {:cmds ["license" "search"] :fn license-search :args->opts [:search-term] :doc "Alias for `license list`."} + {:cmds ["license" "add"] :fn add-license :args->opts [:license] + :doc "Write a license file." + :require [:license] + :spec {:license {:ref "" :desc "License key (e.g. epl-1.0, mit, unlicense)."} + :file {:ref "" :desc "File to write. Defaults to LICENSE."}} + :order [:license :file :help]} + + {:cmds ["new"] :fn new/run-deps-new + :args->opts [:template :name :target-dir] + :doc "Create a project from a deps-new template." + :spec {:name {:coerce proj/coerce-project-name}}} + + {:cmds ["version"] :fn neil-version/neil-version + :doc "Show or manage the project version." + ;; declare version options once here, inherited by the subcommands below + :spec (reduce-kv (fn [m k v] (assoc m k (assoc v :inherit true))) + {} neil-version/version-spec)} + {:cmds ["version" "tag"] :fn (partial neil-version/neil-version :tag) :doc "Tag the current version in git."} + {:cmds ["version" "set"] :fn (partial neil-version/neil-version :set) :args->opts [:version] :doc "Set the project version."} + {:cmds ["version" "major"] :fn (partial neil-version/neil-version :major) :args->opts [:version] :doc "Bump the major version."} + {:cmds ["version" "minor"] :fn (partial neil-version/neil-version :minor) :args->opts [:version] :doc "Bump the minor version."} + {:cmds ["version" "patch"] :fn (partial neil-version/neil-version :patch) :args->opts [:version] :doc "Bump the patch version."} + + {:cmds ["test"] :fn neil-test :spec neil-test/neil-test-spec :alias neil-test/neil-test-aliases + :doc "Run tests (assumes `neil add test`)."} + + {:cmds ["help"] :fn print-help :doc "Show this help."} + {:cmds [] + :spec {:version {:coerce :boolean}} + :fn (fn [{:keys [opts] :as m}] + (if (:version opts) + (neil-version/print-version) + (print-help m))) + :epilog "Most subcommands also accept --alias and --deps-file. Run `neil --help` for a command's options."}]) + (defn -main [& _args] - (cli/dispatch - [{:cmds ["add" "dep"] :fn dep-add :args->opts [:lib]} - {:cmds ["add" "test"] :fn add-cognitect-test-runner} - {:cmds ["add" "build"] :fn add-build} - {:cmds ["add" "kaocha"] :fn add-kaocha} - {:cmds ["add" "nrepl"] :fn add-nrepl} - {:cmds ["add"] :fn print-help} - {:cmds ["dep" "versions"] :fn dep-versions :args->opts [:lib]} - {:cmds ["dep" "add"] :fn dep-add :args->opts [:lib]} - {:cmds ["dep" "search"] :fn dep-search :args->opts [:search-term]} - {:cmds ["dep" "upgrade"] :fn dep-upgrade} - {:cmds ["dep" "update"] :fn dep-upgrade} ;; supported as an alias - {:cmds ["license" "list"] :fn license-search :args->opts [:search-term]} - {:cmds ["license" "search"] :fn license-search :args->opts [:search-term]} - {:cmds ["license" "add"] :fn add-license :args->opts [:license]} - {:cmds ["new"] :fn new/run-deps-new - :args->opts [:template :name :target-dir] - :spec {:name {:coerce proj/coerce-project-name}}} - {:cmds ["version" "tag"] - :fn (partial neil-version/neil-version :tag) - :aliases {:h :help} - :spec neil-version/version-spec} - {:cmds ["version" "set"] - :fn (partial neil-version/neil-version :set) - :args->opts [:version] - :spec neil-version/version-spec - :aliases {:h :help}} - {:cmds ["version" "major"] - :fn (partial neil-version/neil-version :major) - :args->opts [:version] - :spec neil-version/version-spec - :aliases {:h :help}} - {:cmds ["version" "minor"] - :fn (partial neil-version/neil-version :minor) - :args->opts [:version] - :spec neil-version/version-spec - :aliases {:h :help}} - {:cmds ["version" "patch"] - :fn (partial neil-version/neil-version :patch) - :spec neil-version/version-spec - :args->opts [:version] - :aliases {:h :help}} - {:cmds ["version"] - :fn neil-version/neil-version - :aliases {:h :help} - :spec neil-version/version-spec} - {:cmds ["help"] :fn print-help} - {:cmds ["test"] :fn neil-test - :spec neil-test/neil-test-spec - :alias neil-test/neil-test-aliases} - {:cmds [] - :spec {:version {:coerce :boolean}} - :fn (fn [{:keys [opts] :as m}] - (if (:version opts) - (neil-version/print-version) - (print-help m)))}] - *command-line-args* - {:spec spec - :exec-args {:deps-file "deps.edn"}}) + (cli/dispatch dispatch-table + *command-line-args* + {:spec spec + :exec-args {:deps-file "deps.edn"} + :prog "neil" + :help true}) nil) (when (= *file* (System/getProperty "babashka.file")) diff --git a/prelude b/prelude index 9806c4a..5de11a6 100755 --- a/prelude +++ b/prelude @@ -11,6 +11,10 @@ io.github.seancorfield/deps-new {:git/url "https://github.com/seancorfield/deps-new" :git/tag "v0.10.3" :git/sha "2c012c4"} - org.babashka/cli {:mvn/version "0.8.58"} + org.babashka/cli {:git/url "https://github.com/babashka/cli" + :git/sha "de19235af79da89c57696d5079a8a9899fd66229"} version-clj/version-clj {:mvn/version "2.0.2"} org.babashka/http-client {:mvn/version "0.1.4"}}}) + +;; force loading the add-deps cli over the bb built-in (needs format-command-help) +(require '[babashka.cli] :reload) diff --git a/src/babashka/neil.clj b/src/babashka/neil.clj index 6e83c10..56473a8 100644 --- a/src/babashka/neil.clj +++ b/src/babashka/neil.clj @@ -767,6 +767,7 @@ chmod +x bin/kaocha {:cmds ["dep" "search"] :fn dep-search :args->opts [:search-term] :doc "Search Clojars for a string in any attribute of an artifact." :require [:search-term] + :spec {:search-term {:coerce :string}} :epilog (str/trim " Examples: diff --git a/test/babashka/neil/dep_upgrade_test.clj b/test/babashka/neil/dep_upgrade_test.clj index 0a340b0..ab8bc79 100644 --- a/test/babashka/neil/dep_upgrade_test.clj +++ b/test/babashka/neil/dep_upgrade_test.clj @@ -186,8 +186,8 @@ (testing "upgrading an alias's :extra-deps works as expected" (spit test-file-path "{}") ;; here we add the same dep to two aliases - (test-util/neil "dep add :lib clj-kondo/clj-kondo --alias lint --sha 6ffc3934cb83d2c4fff16d84198c73b40cd8a078" :deps-file test-file-path) - (test-util/neil "dep add :lib clj-kondo/clj-kondo --alias other-lint --version 2020.01.01" :deps-file test-file-path) + (test-util/neil "dep add :lib clj-kondo/clj-kondo :alias lint :sha 6ffc3934cb83d2c4fff16d84198c73b40cd8a078" :deps-file test-file-path) + (test-util/neil "dep add :lib clj-kondo/clj-kondo :alias other-lint :version 2020.01.01" :deps-file test-file-path) (let [initial-versions (get-alias-versions 'clj-kondo/clj-kondo)] (test-util/neil "dep upgrade" :deps-file test-file-path) (let [upgraded-versions (get-alias-versions 'clj-kondo/clj-kondo) @@ -204,7 +204,7 @@ (spit test-file-path "{}") ;; here we add the same dep to two aliases (test-util/neil (str "dep add :lib clj-kondo/clj-kondo" - " --alias lint --sha 6ffc3934cb83d2c4fff16d84198c73b40cd8a078") + " :alias lint :sha 6ffc3934cb83d2c4fff16d84198c73b40cd8a078") :deps-file test-file-path) (test-util/neil "dep add :lib babashka/fs :version 0.0.1" :deps-file test-file-path) (let [initial-clj-kondo-v (first (get-alias-versions 'clj-kondo/clj-kondo)) diff --git a/tests.clj b/tests.clj index a0d4f4c..5761c2c 100644 --- a/tests.clj +++ b/tests.clj @@ -74,8 +74,7 @@ "We're able to find exactly 3 hiccup versions")) (deftest dep-search-help-test - (doseq [cmd ["./neil dep search" - "./neil dep search --help" + (doseq [cmd ["./neil dep search --help" "./neil dep search foo --help"]] (let [{:keys [out]} @(process cmd {:out :string})] (is (str/starts-with? out "Usage: neil dep search "))))) @@ -94,12 +93,8 @@ (is (any? (run-dep-subcommand "search" "babashka nrepl"))) (is (thrown-with-msg? Exception #"Unable to find" (run-dep-subcommand "search" "%22searchTermThatIsn'tFound"))) - (is (some #(str/starts-with? % "Usage: neil dep search") - (run-dep-subcommand "search" "42")) - "passing non-string shows help") - (is (some #(str/starts-with? % "Usage: neil dep search") - (run-dep-subcommand "search" " ")) - "passing blank string shows help") + ;; numeric-looking term is coerced to a string, not a number (would crash url-encode) + (is (not-empty (run-dep-subcommand "search" "42"))) (is (some (partial re-matches @@ -132,7 +127,7 @@ (run-license out-file "add" ":license epl-2.0") (is (str/includes? (slurp out-file) "Eclipse Public License"))) (testing "missing license key errors" - (is (thrown-with-msg? Exception #"No license key" + (is (thrown-with-msg? Exception #"Required option: --license" (run-license out-file "add")))) (testing "invalid license key errors" (is (thrown-with-msg? Exception #"nonExistentLicense" From 3e3899d67a3125e90fc1e4012492172c0980f914 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Thu, 4 Jun 2026 23:26:26 +0200 Subject: [PATCH 07/13] Move changelog entry to Unreleased --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad1f9c2..94da0d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,12 @@ See the [New Clojure project quickstart](https://blog.michielborkent.nl/new-clojure-project-quickstart.html) blog post for a gentle introduction into `neil`. -## 0.3.70 +## Unreleased - Auto-generated CLI help via babashka.cli + +## 0.3.70 + - [#261](https://github.com/babashka/neil/issues/261): `neil dep upgrade` now upgrades unstable deps (e.g. release candidates) to a newer unstable version when no newer stable version exists - `neil dep search` for a fully-qualified lib (e.g. `org.clojure/clojurescript`) no longer fails: a `/` in the Solr query made central.sonatype.com return a 500 - [#258](https://github.com/babashka/neil/issues/258): `neil test` now exits with non-zero exit code when tests fail From 68a685e07ecce2b9d3d67084170e42fd462ef39b Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Thu, 4 Jun 2026 23:33:08 +0200 Subject: [PATCH 08/13] Trim root epilog; bump cli (epilog now footer) --- deps.edn | 2 +- src/babashka/neil.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deps.edn b/deps.edn index 0353b7c..bdab4a7 100644 --- a/deps.edn +++ b/deps.edn @@ -4,7 +4,7 @@ rewrite-clj/rewrite-clj {:mvn/version "1.1.46"} borkdude/rewrite-edn {:mvn/version "0.4.8"} org.babashka/cli {:git/url "https://github.com/babashka/cli" - :git/sha "de19235af79da89c57696d5079a8a9899fd66229"} + :git/sha "7c171d7bbc720266e8e4c52edda45a28a103bb81"} cheshire/cheshire {:mvn/version "5.11.0"} io.github.seancorfield/deps-new {:git/url "https://github.com/seancorfield/deps-new" :git/tag "v0.10.3" diff --git a/src/babashka/neil.clj b/src/babashka/neil.clj index 56473a8..436a672 100644 --- a/src/babashka/neil.clj +++ b/src/babashka/neil.clj @@ -836,7 +836,7 @@ Examples: (if (:version opts) (neil-version/print-version) (print-help m))) - :epilog "Most subcommands also accept --alias and --deps-file. Run `neil --help` for a command's options."}]) + :epilog "Most subcommands also accept --alias and --deps-file."}]) (defn -main [& _args] (cli/dispatch dispatch-table From 5d3ed3b242824374729cbab8fa4c4890942a4e67 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 5 Jun 2026 00:03:32 +0200 Subject: [PATCH 09/13] Bump cli to main SHA (format-opts 2-column layout) --- deps.edn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.edn b/deps.edn index bdab4a7..f2d7ecb 100644 --- a/deps.edn +++ b/deps.edn @@ -4,7 +4,7 @@ rewrite-clj/rewrite-clj {:mvn/version "1.1.46"} borkdude/rewrite-edn {:mvn/version "0.4.8"} org.babashka/cli {:git/url "https://github.com/babashka/cli" - :git/sha "7c171d7bbc720266e8e4c52edda45a28a103bb81"} + :git/sha "45cf0775e6348020cfc0f63390bc30c56f467eff"} cheshire/cheshire {:mvn/version "5.11.0"} io.github.seancorfield/deps-new {:git/url "https://github.com/seancorfield/deps-new" :git/tag "v0.10.3" From 5527b80ce2284be5cd574949b6fb665953814546 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 5 Jun 2026 00:08:32 +0200 Subject: [PATCH 10/13] Bump cli, update help output --- README.md | 7 +++---- neil | 4 ++-- prelude | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e2cb5a5..2f296e3 100644 --- a/README.md +++ b/README.md @@ -71,12 +71,11 @@ Commands: help Show this help. Options: - --version - -h, --help Show this help - -Most subcommands also accept --alias and --deps-file. Run `neil --help` for a command's options. + --version Run "neil --help" for more information on a command. + +Most subcommands also accept --alias and --deps-file. ``` ### add dep diff --git a/neil b/neil index 9116f7d..7628df4 100755 --- a/neil +++ b/neil @@ -12,7 +12,7 @@ :git/tag "v0.10.3" :git/sha "2c012c4"} org.babashka/cli {:git/url "https://github.com/babashka/cli" - :git/sha "de19235af79da89c57696d5079a8a9899fd66229"} + :git/sha "45cf0775e6348020cfc0f63390bc30c56f467eff"} version-clj/version-clj {:mvn/version "2.0.2"} org.babashka/http-client {:mvn/version "0.1.4"}}}) @@ -1716,7 +1716,7 @@ Examples: (if (:version opts) (neil-version/print-version) (print-help m))) - :epilog "Most subcommands also accept --alias and --deps-file. Run `neil --help` for a command's options."}]) + :epilog "Most subcommands also accept --alias and --deps-file."}]) (defn -main [& _args] (cli/dispatch dispatch-table diff --git a/prelude b/prelude index 5de11a6..82ff070 100755 --- a/prelude +++ b/prelude @@ -12,7 +12,7 @@ :git/tag "v0.10.3" :git/sha "2c012c4"} org.babashka/cli {:git/url "https://github.com/babashka/cli" - :git/sha "de19235af79da89c57696d5079a8a9899fd66229"} + :git/sha "45cf0775e6348020cfc0f63390bc30c56f467eff"} version-clj/version-clj {:mvn/version "2.0.2"} org.babashka/http-client {:mvn/version "0.1.4"}}}) From 6ba212129b8d8ad04b7ef8a44ada261e36d24bd6 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 5 Jun 2026 00:13:23 +0200 Subject: [PATCH 11/13] Require lib/version for add dep and version set --- neil | 4 ++-- src/babashka/neil.clj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/neil b/neil index 7628df4..32a00df 100755 --- a/neil +++ b/neil @@ -1632,7 +1632,7 @@ chmod +x bin/kaocha (def dispatch-table [{:cmds ["add"] :doc "Add a dep, test runner, build or nREPL alias."} - {:cmds ["add" "dep"] :fn dep-add :args->opts [:lib] :doc "Alias for `dep add`."} + {:cmds ["add" "dep"] :fn dep-add :args->opts [:lib] :require [:lib] :doc "Alias for `dep add`."} {:cmds ["add" "test"] :fn add-cognitect-test-runner :doc "Add the cognitect test runner to the :test alias."} {:cmds ["add" "build"] :fn add-build :doc "Add a tools.build build.clj and :build alias."} {:cmds ["add" "kaocha"] :fn add-kaocha :doc "Add the kaocha test runner to the :kaocha alias."} @@ -1701,7 +1701,7 @@ Examples: :spec (reduce-kv (fn [m k v] (assoc m k (assoc v :inherit true))) {} neil-version/version-spec)} {:cmds ["version" "tag"] :fn (partial neil-version/neil-version :tag) :doc "Tag the current version in git."} - {:cmds ["version" "set"] :fn (partial neil-version/neil-version :set) :args->opts [:version] :doc "Set the project version."} + {:cmds ["version" "set"] :fn (partial neil-version/neil-version :set) :args->opts [:version] :require [:version] :doc "Set the project version."} {:cmds ["version" "major"] :fn (partial neil-version/neil-version :major) :args->opts [:version] :doc "Bump the major version."} {:cmds ["version" "minor"] :fn (partial neil-version/neil-version :minor) :args->opts [:version] :doc "Bump the minor version."} {:cmds ["version" "patch"] :fn (partial neil-version/neil-version :patch) :args->opts [:version] :doc "Bump the patch version."} diff --git a/src/babashka/neil.clj b/src/babashka/neil.clj index 436a672..29415b2 100644 --- a/src/babashka/neil.clj +++ b/src/babashka/neil.clj @@ -752,7 +752,7 @@ chmod +x bin/kaocha (def dispatch-table [{:cmds ["add"] :doc "Add a dep, test runner, build or nREPL alias."} - {:cmds ["add" "dep"] :fn dep-add :args->opts [:lib] :doc "Alias for `dep add`."} + {:cmds ["add" "dep"] :fn dep-add :args->opts [:lib] :require [:lib] :doc "Alias for `dep add`."} {:cmds ["add" "test"] :fn add-cognitect-test-runner :doc "Add the cognitect test runner to the :test alias."} {:cmds ["add" "build"] :fn add-build :doc "Add a tools.build build.clj and :build alias."} {:cmds ["add" "kaocha"] :fn add-kaocha :doc "Add the kaocha test runner to the :kaocha alias."} @@ -821,7 +821,7 @@ Examples: :spec (reduce-kv (fn [m k v] (assoc m k (assoc v :inherit true))) {} neil-version/version-spec)} {:cmds ["version" "tag"] :fn (partial neil-version/neil-version :tag) :doc "Tag the current version in git."} - {:cmds ["version" "set"] :fn (partial neil-version/neil-version :set) :args->opts [:version] :doc "Set the project version."} + {:cmds ["version" "set"] :fn (partial neil-version/neil-version :set) :args->opts [:version] :require [:version] :doc "Set the project version."} {:cmds ["version" "major"] :fn (partial neil-version/neil-version :major) :args->opts [:version] :doc "Bump the major version."} {:cmds ["version" "minor"] :fn (partial neil-version/neil-version :minor) :args->opts [:version] :doc "Bump the minor version."} {:cmds ["version" "patch"] :fn (partial neil-version/neil-version :patch) :args->opts [:version] :doc "Bump the patch version."} From a80ddac261f160e7bbe474ccc1a0a09cffe9ac5f Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Fri, 5 Jun 2026 00:18:34 +0200 Subject: [PATCH 12/13] Drop redundant epilog (subcommand help lists --alias/--deps-file) --- README.md | 2 -- neil | 3 +-- src/babashka/neil.clj | 3 +-- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2f296e3..e4831dc 100644 --- a/README.md +++ b/README.md @@ -74,8 +74,6 @@ Options: --version Run "neil --help" for more information on a command. - -Most subcommands also accept --alias and --deps-file. ``` ### add dep diff --git a/neil b/neil index 32a00df..bbe4f8b 100755 --- a/neil +++ b/neil @@ -1715,8 +1715,7 @@ Examples: :fn (fn [{:keys [opts] :as m}] (if (:version opts) (neil-version/print-version) - (print-help m))) - :epilog "Most subcommands also accept --alias and --deps-file."}]) + (print-help m)))}]) (defn -main [& _args] (cli/dispatch dispatch-table diff --git a/src/babashka/neil.clj b/src/babashka/neil.clj index 29415b2..157dc61 100644 --- a/src/babashka/neil.clj +++ b/src/babashka/neil.clj @@ -835,8 +835,7 @@ Examples: :fn (fn [{:keys [opts] :as m}] (if (:version opts) (neil-version/print-version) - (print-help m))) - :epilog "Most subcommands also accept --alias and --deps-file."}]) + (print-help m)))}]) (defn -main [& _args] (cli/dispatch dispatch-table From cdfdeccc57cf03c1a9d0cf8532e74c30b46b2fa8 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Sat, 6 Jun 2026 14:15:17 +0200 Subject: [PATCH 13/13] Bump babashka.cli to released 0.10.69 --- deps.edn | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/deps.edn b/deps.edn index f2d7ecb..caef16e 100644 --- a/deps.edn +++ b/deps.edn @@ -3,8 +3,7 @@ babashka/process {:mvn/version "0.4.16"} rewrite-clj/rewrite-clj {:mvn/version "1.1.46"} borkdude/rewrite-edn {:mvn/version "0.4.8"} - org.babashka/cli {:git/url "https://github.com/babashka/cli" - :git/sha "45cf0775e6348020cfc0f63390bc30c56f467eff"} + org.babashka/cli {:mvn/version "0.10.69"} cheshire/cheshire {:mvn/version "5.11.0"} io.github.seancorfield/deps-new {:git/url "https://github.com/seancorfield/deps-new" :git/tag "v0.10.3"