Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

neil dep upgrade #110

Merged
merged 40 commits into from
Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
720f54c
dep-upgrade outline
teodorlu Aug 16, 2022
a529e90
Experiment, print latest versions
teodorlu Aug 16, 2022
4b665b1
Comment-out unused binding (CI)
teodorlu Aug 16, 2022
cf984e0
Also regenerate script
teodorlu Aug 16, 2022
845ff0a
Remove comments
teodorlu Aug 16, 2022
6cac9fe
Query for latest in parallell
teodorlu Aug 16, 2022
03222ef
Works without :dry-run
teodorlu Aug 16, 2022
5cae2c0
Cleanup whitespace & comments
teodorlu Aug 16, 2022
9c955ec
Try updating single dependency, hit option problem
teodorlu Aug 16, 2022
6cb126c
:lib must be symbol!!!
teodorlu Aug 16, 2022
36d4657
Add changelog entry
teodorlu Aug 16, 2022
d4b2694
Update->upgrade
teodorlu Aug 16, 2022
9bf493c
Try writing some tests -- but other tests failing
teodorlu Aug 16, 2022
4f55ce1
Merge branch 'main' into dep-upgrade
teodorlu Aug 19, 2022
28593d3
Move `dep upgrade` tests to own namespace
teodorlu Aug 19, 2022
8312682
Use neil from `babashka.neil.test-util`
teodorlu Aug 21, 2022
ffc6cbc
Whoops, more neil
teodorlu Aug 21, 2022
8ee3528
Also use `test-file` from `test-util`
teodorlu Aug 21, 2022
131bfc1
First passing test 🎉
teodorlu Aug 21, 2022
b681108
Use github token if found, cleanup
teodorlu Aug 21, 2022
3d08a4f
Rewrite tests to not use :dry-run
teodorlu Aug 21, 2022
410aff4
Remove unused dep
teodorlu Aug 21, 2022
1eea1ef
Merge remote-tracking branch 'upstream/main' into dep-upgrade
teodorlu Aug 24, 2022
40f65f1
Cleanup: remove comments
teodorlu Aug 24, 2022
ab118dc
Improve comment
teodorlu Aug 24, 2022
e7f1cb4
fix: support :deps-file, :dry-run in test-util/neil
russmatney Oct 12, 2022
fb8c108
fix: ignore unused _all in neil/dep-upgrade
russmatney Oct 12, 2022
b69c592
Merge: Dep Upgrade: fix tests and linter errors
russmatney Oct 12, 2022
483b08d
Merge remote-tracking branch 'origin/main' into dep-upgrade
russmatney Oct 12, 2022
b2b0f73
Trigger CI
teodorlu Oct 12, 2022
29d098b
Regenerate `neil` script
teodorlu Oct 12, 2022
3f318c9
Remove comment to trigger CI
teodorlu Oct 12, 2022
ab99ce4
docs: initial help text for upgrade command
russmatney Oct 12, 2022
67f7703
docs: nest upgrade in `dep` properly
russmatney Oct 12, 2022
f63c4d6
test: add test for updating a single dep
russmatney Oct 12, 2022
8032039
test: coverage for :git/sha dep upgrades
russmatney Oct 12, 2022
6738ecc
fix: maintain :git/sha based deps across upgrades
russmatney Oct 12, 2022
83e84c1
feat: support `update` as alias of `upgrade`
russmatney Oct 12, 2022
77f1b90
feat: print when performing an upgrade
russmatney Oct 12, 2022
d20cdf8
fix: rely on :git/sha rather than :git/url
russmatney Oct 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- Introduce `neil dep upgrade` API for upgrading existing dependencies

## 0.1.46

- Add `neil version` subcommands
Expand Down
97 changes: 94 additions & 3 deletions neil
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,14 @@

(defn url-encode [s] (URLEncoder/encode s "UTF-8"))

(def dev-github-user (System/getenv "BABASHKA_NEIL_DEV_GITHUB_USER"))
(def dev-github-token (System/getenv "BABASHKA_NEIL_DEV_GITHUB_TOKEN"))

(def curl-opts
{:throw false
:compressed (not (fs/windows?))})
(merge {:throw false
:compressed (not (fs/windows?))}
(when (and dev-github-user dev-github-token)
{:basic-auth [dev-github-user dev-github-token]})))

(defn curl-get-json [url]
(-> (curl/get url curl-opts)
Expand Down Expand Up @@ -790,7 +795,8 @@ using the [major|minor|patch] subcommands.
:deps-file {:ref "<file>"
:desc "Add to <file> instead of deps.edn."
:default "deps.edn"}
:limit {:coerce :long}})
:limit {:coerce :long}
:dry-run {:coerce :boolean}})

(def windows? (fs/windows?))

Expand Down Expand Up @@ -1181,6 +1187,81 @@ will return libraries with 'test framework' in their description.")))
:version (:version search-result)
:description (pr-str (:description search-result)))))))


(defn lib+current->latest
"Expects a lib symbol like `'babashka/fs` and a dep description
like `{:mvn/version \"some-version\"}` or `{:git/sha \"sha-blah\"}`.

Returns a dep description of the same form (a map with `:mvn/version` or `:git/sha`)
with the latest version or latest sha."
[lib current-dep]
(cond (:git/sha current-dep)
(when-let [sha (git/latest-github-sha lib)]
{:git/sha sha})

(:mvn/version current-dep)
(when-let [version (or (latest-clojars-version lib)
(latest-mvn-version lib))]
{:mvn/version version})))

(defn do-dep-upgrade
"Updates the deps version in deps.edn for a single lib with the version passed in `latest`.
First compares `current` and `latest`.
Supports `:dry-run` in the passed `opts`."
[opts lib current {:keys [git/sha mvn/version] :as _latest}]
(when (or (and sha (not= (:git/sha current) sha))
(and version (not= (:mvn/version current) version)))
(if (:dry-run opts)
(if sha
(println "Would upgrade" :lib lib :version (:git/sha current) :latest-sha sha)
(println "Would upgrade" :lib lib :version (:mvn/version current) :latest-version version))
(do
(if sha
(println "Upgrading" :lib lib :version (:git/sha current) :latest-sha sha)
(println "Upgrading" :lib lib :version (:mvn/version current) :latest-version version))
(dep-add {:opts (cond-> opts
lib (assoc :lib lib)
version (assoc :version version)
sha (assoc :sha sha))})))))

(defn dep-upgrade [{:keys [opts] :as _all}]
(if (:lib opts)
;; upgrade a single dependency
(let [lib (:lib opts)
lib (symbol lib)
current (-> (edn-string opts)
edn/read-string
:deps
(get lib))
latest (when current (lib+current->latest lib current))]
(cond (not current)
(binding [*out* *err*]
(println "Local dependency not found:" lib)
(println "Use `neil dep add` to add dependencies.")
(System/exit 1))

(and (not (:git/sha latest)) (not (:mvn/version latest)))
(binding [*out* *err*]
(println "No remote version found for" lib)
(System/exit 1))

:else (do-dep-upgrade opts lib current latest)))

;; upgrade multiple dependencies
(let [current-deps (-> (edn-string opts)
edn/read-string
:deps)
latest-deps (->> current-deps
(pmap (fn [[lib current]]
[lib (lib+current->latest lib current)]))
(filter (fn [[_lib new-version]]
(some? new-version)))
(into {}))]
(doseq [[lib current] current-deps]
(let [latest (get latest-deps lib)]
(do-dep-upgrade opts lib current latest))))))


(defn print-help [_]
(println (str/trim "
Usage: neil <subcommand> <options>
Expand All @@ -1205,6 +1286,14 @@ dep
search: Search Clojars for a string in any attribute of an artifact
Run `neil dep search --help` to see all options.

upgrade: Upgrade all libs in the deps.edn file.
Supports --lib <libname> or :lib <libname> for upgrading a single, specified lib.
Supports --dry-run for printing updates without updating the deps.edn file.
Ex: `neil dep upgrade` - upgrade all deps.
Ex: `neil dep upgrade --dry-run` - print deps that would be upgraded.
Ex: `neil dep upgrade :lib clj-kondo/clj-kondo` - update a single dep.
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`
Expand Down Expand Up @@ -1282,6 +1371,8 @@ test
{: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]}
Expand Down
88 changes: 87 additions & 1 deletion src/babashka/neil.clj
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
:deps-file {:ref "<file>"
:desc "Add to <file> instead of deps.edn."
:default "deps.edn"}
:limit {:coerce :long}})
:limit {:coerce :long}
:dry-run {:coerce :boolean}})

(def windows? (fs/windows?))

Expand Down Expand Up @@ -418,6 +419,81 @@ will return libraries with 'test framework' in their description.")))
:version (:version search-result)
:description (pr-str (:description search-result)))))))


(defn lib+current->latest
"Expects a lib symbol like `'babashka/fs` and a dep description
like `{:mvn/version \"some-version\"}` or `{:git/sha \"sha-blah\"}`.

Returns a dep description of the same form (a map with `:mvn/version` or `:git/sha`)
with the latest version or latest sha."
[lib current-dep]
(cond (:git/sha current-dep)
(when-let [sha (git/latest-github-sha lib)]
{:git/sha sha})

(:mvn/version current-dep)
(when-let [version (or (latest-clojars-version lib)
(latest-mvn-version lib))]
{:mvn/version version})))

(defn do-dep-upgrade
"Updates the deps version in deps.edn for a single lib with the version passed in `latest`.
First compares `current` and `latest`.
Supports `:dry-run` in the passed `opts`."
[opts lib current {:keys [git/sha mvn/version] :as _latest}]
(when (or (and sha (not= (:git/sha current) sha))
(and version (not= (:mvn/version current) version)))
(if (:dry-run opts)
(if sha
(println "Would upgrade" :lib lib :version (:git/sha current) :latest-sha sha)
(println "Would upgrade" :lib lib :version (:mvn/version current) :latest-version version))
(do
(if sha
(println "Upgrading" :lib lib :version (:git/sha current) :latest-sha sha)
(println "Upgrading" :lib lib :version (:mvn/version current) :latest-version version))
(dep-add {:opts (cond-> opts
lib (assoc :lib lib)
version (assoc :version version)
sha (assoc :sha sha))})))))

(defn dep-upgrade [{:keys [opts] :as _all}]
(if (:lib opts)
;; upgrade a single dependency
(let [lib (:lib opts)
lib (symbol lib)
current (-> (edn-string opts)
edn/read-string
:deps
(get lib))
latest (when current (lib+current->latest lib current))]
(cond (not current)
(binding [*out* *err*]
(println "Local dependency not found:" lib)
(println "Use `neil dep add` to add dependencies.")
(System/exit 1))

(and (not (:git/sha latest)) (not (:mvn/version latest)))
(binding [*out* *err*]
(println "No remote version found for" lib)
(System/exit 1))

:else (do-dep-upgrade opts lib current latest)))

;; upgrade multiple dependencies
(let [current-deps (-> (edn-string opts)
edn/read-string
:deps)
latest-deps (->> current-deps
(pmap (fn [[lib current]]
[lib (lib+current->latest lib current)]))
(filter (fn [[_lib new-version]]
(some? new-version)))
(into {}))]
(doseq [[lib current] current-deps]
(let [latest (get latest-deps lib)]
(do-dep-upgrade opts lib current latest))))))


(defn print-help [_]
(println (str/trim "
Usage: neil <subcommand> <options>
Expand All @@ -442,6 +518,14 @@ dep
search: Search Clojars for a string in any attribute of an artifact
Run `neil dep search --help` to see all options.

upgrade: Upgrade all libs in the deps.edn file.
Supports --lib <libname> or :lib <libname> for upgrading a single, specified lib.
Supports --dry-run for printing updates without updating the deps.edn file.
Ex: `neil dep upgrade` - upgrade all deps.
Ex: `neil dep upgrade --dry-run` - print deps that would be upgraded.
Ex: `neil dep upgrade :lib clj-kondo/clj-kondo` - update a single dep.
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`
Expand Down Expand Up @@ -519,6 +603,8 @@ test
{: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]}
Expand Down
9 changes: 7 additions & 2 deletions src/babashka/neil/curl.clj
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@

(defn url-encode [s] (URLEncoder/encode s "UTF-8"))

(def dev-github-user (System/getenv "BABASHKA_NEIL_DEV_GITHUB_USER"))
(def dev-github-token (System/getenv "BABASHKA_NEIL_DEV_GITHUB_TOKEN"))

(def curl-opts
{:throw false
:compressed (not (fs/windows?))})
(merge {:throw false
:compressed (not (fs/windows?))}
(when (and dev-github-user dev-github-token)
{:basic-auth [dev-github-user dev-github-token]})))

(defn curl-get-json [url]
(-> (curl/get url curl-opts)
Expand Down
89 changes: 89 additions & 0 deletions test/babashka/neil/dep_upgrade_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
(ns babashka.neil.dep-upgrade-test
(:require
[babashka.neil.test-util :as test-util]
[clojure.test :as t :refer [deftest is testing]]
[clojure.edn :as edn]))

(def test-file-path (str (test-util/test-file "deps.edn")))
(defn get-dep-version [dep-name]
(-> test-file-path slurp edn/read-string :deps (get dep-name)))

(deftest dep-upgrade-test
(testing "a fresh project is up-to-date"
(spit test-file-path "{}")
(test-util/neil "dep add :lib clj-kondo/clj-kondo" :deps-file test-file-path)
(let [clj-kondo-version-original (get-dep-version 'clj-kondo/clj-kondo)]
(test-util/neil "dep upgrade" :deps-file test-file-path)
(is (= clj-kondo-version-original (get-dep-version 'clj-kondo/clj-kondo)))))

(testing "an old dependency can be upgraded, and --dry-run is respected"
(spit test-file-path "{}")
(test-util/neil "dep add :lib clj-kondo/clj-kondo :version 2022.01.01" :deps-file test-file-path)
(let [clj-kondo-version-original (get-dep-version 'clj-kondo/clj-kondo)]

;; should be the same version after --dry-run
(test-util/neil "dep upgrade" :deps-file test-file-path :dry-run true)
(is (= clj-kondo-version-original (get-dep-version 'clj-kondo/clj-kondo)))

;; after a non-dry-run, the version should be changed
(test-util/neil "dep upgrade" :deps-file test-file-path)
(is (not (= clj-kondo-version-original (get-dep-version 'clj-kondo/clj-kondo)))))))

(deftest dep-upgrade-test-one-lib
(testing "specifying :lib only updates one dep"
(spit test-file-path "{}")
(test-util/neil "dep add :lib clj-kondo/clj-kondo :version 2022.01.01" :deps-file test-file-path)
(test-util/neil "dep add :lib babashka/fs :version 0.0.1" :deps-file test-file-path)
(let [clj-kondo-original (get-dep-version 'clj-kondo/clj-kondo)
fs-original (get-dep-version 'babashka/fs)]
(test-util/neil "dep upgrade :lib clj-kondo/clj-kondo" :deps-file test-file-path)
(let [clj-kondo-upgraded (get-dep-version 'clj-kondo/clj-kondo)
fs-upgraded (get-dep-version 'babashka/fs)]
(is (not (= clj-kondo-original clj-kondo-upgraded)))
(is (= fs-original fs-upgraded))))))

(deftest dep-upgrade-test-maintain-dep-source
(testing "upgrading a :git/sha dep should maintain :git/sha"
(spit test-file-path "{}")
(test-util/neil "dep add :lib clj-kondo/clj-kondo :latest-sha true" :deps-file test-file-path)
(let [clj-kondo-original (get-dep-version 'clj-kondo/clj-kondo)]
;; this upgrade should return the same latest sha, NOT a :mvn/version
(test-util/neil "dep upgrade" :deps-file test-file-path)
(let [{:keys [git/url git/sha]} (get-dep-version 'clj-kondo/clj-kondo)]
(is url)
(is sha)
(is (= clj-kondo-original (get-dep-version 'clj-kondo/clj-kondo))))))

(testing "upgrading an older :git/sha dep should set the latest :git/sha"
(spit test-file-path "{}")
(test-util/neil "dep add :lib clj-kondo/clj-kondo :sha 6ffc3934cb83d2c4fff16d84198c73b40cd8a078"
:deps-file test-file-path)
(let [clj-kondo-original (get-dep-version 'clj-kondo/clj-kondo)]
;; here we upgrade and then assert that the sha is different, but still :git/sha based
(test-util/neil "dep upgrade" :deps-file test-file-path)
(let [{:keys [git/url git/sha]} (get-dep-version 'clj-kondo/clj-kondo)]
(is url)
(is sha)
;; should be a different sha
(is (not (= sha (:git/sha clj-kondo-original)))))))

(testing "upgrading a single lib should also maintain :git/url and sha"
(spit test-file-path "{}")
(test-util/neil "dep add :lib clj-kondo/clj-kondo :sha 6ffc3934cb83d2c4fff16d84198c73b40cd8a078"
:deps-file test-file-path)
(test-util/neil "dep add :lib babashka/fs :sha 791009052fe8916b4e10e55732622a69250c7598"
:deps-file test-file-path)

(let [clj-kondo-original (get-dep-version 'clj-kondo/clj-kondo)
fs-original (get-dep-version 'babashka/fs)]
(test-util/neil "dep upgrade :lib babashka/fs" :deps-file test-file-path)
(let [clj-kondo-upgraded (get-dep-version 'clj-kondo/clj-kondo)
fs-upgraded (get-dep-version 'babashka/fs)]
(is (:git/sha clj-kondo-upgraded))
(is (:git/url clj-kondo-upgraded))
(is (:git/sha fs-upgraded))
(is (:git/url fs-upgraded))
;; should be unchanged
(is (= clj-kondo-original clj-kondo-upgraded))
;; should be a different sha
(is (not (= fs-original fs-upgraded)))))))
3 changes: 2 additions & 1 deletion test/babashka/neil/test_runner.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

(def test-namespaces
'[tests
babashka.neil.version-test])
babashka.neil.version-test
babashka.neil.dep-upgrade-test])

(doseq [ns test-namespaces]
(require ns))
Expand Down
13 changes: 7 additions & 6 deletions test/babashka/neil/test_util.clj
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@
(defn set-deps-edn! [x]
(spit (test-file "deps.edn") (pr-str x)))

(defn neil [cli-args & {:keys [out] :or {out :string}}]
(let [deps-file (str (test-file "deps.edn"))
cli-args' (concat (if (string? cli-args)
(process/tokenize cli-args)
cli-args)
[:deps-file deps-file])]
(defn neil [cli-args & {:keys [deps-file dry-run out] :or {out :string}}]
(let [backup-deps-file (str (test-file "deps.edn"))
cli-args' (concat (if (string? cli-args)
(process/tokenize cli-args)
cli-args)
[:deps-file (or deps-file backup-deps-file)]
(when dry-run [:dry-run "true"]))]
(binding [*command-line-args* cli-args']
(let [s (with-out-str (tasks/exec `neil-main/-main))]
{:out (if (#{:edn} out) (edn/read-string s) (str/trim s))}))))