diff --git a/CHANGELOG.md b/CHANGELOG.md index f1916e5..94da0d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ See the [New Clojure project quickstart](https://blog.michielborkent.nl/new-clojure-project-quickstart.html) blog post for a gentle introduction into `neil`. +## 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 diff --git a/README.md b/README.md index f2e433b..e4831dc 100644 --- a/README.md +++ b/README.md @@ -59,54 +59,21 @@ 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 -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. - - 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/deps.edn b/deps.edn index d797b7b..caef16e 100644 --- a/deps.edn +++ b/deps.edn @@ -3,12 +3,13 @@ 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 {: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" :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 diff --git a/neil b/neil index 51fe8ef..bbe4f8b 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 "45cf0775e6348020cfc0f63390bc30c56f467eff"} 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,100 @@ 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] :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."} + {: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] :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."} + + {: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)))}]) + (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..82ff070 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 "45cf0775e6348020cfc0f63390bc30c56f467eff"} 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 3970815..157dc61 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,25 +356,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) @@ -478,27 +458,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)) @@ -510,36 +472,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 @@ -586,21 +518,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 +668,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) @@ -793,57 +698,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. - - search: Search Clojars for a string in any attribute of an artifact - Run `neil dep search --help` to see all options. +(declare dispatch-table) - 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") @@ -892,66 +750,100 @@ 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] :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."} + {: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] :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."} + + {: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)))}]) + (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/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"