deps: global gitlibs-style clone cache + :tasks runner
Git clones now default to a shared, sha-immutable cache —
$JOLT_GITLIBS, else <config-dir>/gitlibs — instead of a per-project
./jpm_tree, the tools.gitlibs ~/.gitlibs model. Passing tree
explicitly still works (tests do). The resolved-roots cache moves
out of the clone tree to the project-local .cpcache/jolt-deps.jdn,
since roots depend on the project while clones don't.
deps.edn grows :tasks, the honest subset of babashka's: a string
task is a shell command, a map task is {:main-opts [...] :doc}.
jolt-deps tasks lists them (merged user+project), jolt-deps task
NAME runs one. Bare-expression tasks are out of scope: the reader
hands back parsed data and round-tripping to source is fragile.
Also fixes load-config skipping the symbol-key normalization when
only one config file existed — :tasks/:deps keys stayed raw reader
symbols (which embed positions and never compare equal), so lookups
missed. Regression rows in deps-tasks-test; docs updated for the
whole tools.deps surface (aliases, -A/-M, user config, conflicts,
gitlibs cache, tasks).
This commit is contained in:
parent
0a01afe595
commit
9ab36eb97f
5 changed files with 231 additions and 32 deletions
|
|
@ -63,6 +63,10 @@ jolt-deps path # print the resolved roots (':'-joined)
|
||||||
jolt-deps run FILE [args] # resolve, then run `jolt FILE …`
|
jolt-deps run FILE [args] # resolve, then run `jolt FILE …`
|
||||||
jolt-deps repl # resolve, then start a REPL
|
jolt-deps repl # resolve, then start a REPL
|
||||||
jolt-deps -e EXPR [args] # resolve, then evaluate EXPR
|
jolt-deps -e EXPR [args] # resolve, then evaluate EXPR
|
||||||
|
jolt-deps -A:dev path # include the :dev alias's extra paths/deps
|
||||||
|
jolt-deps -M:test [args] # run the :test alias's :main-opts through jolt
|
||||||
|
jolt-deps tasks # list :tasks from deps.edn
|
||||||
|
jolt-deps task NAME [args] # run a task
|
||||||
```
|
```
|
||||||
|
|
||||||
`jolt-deps` launches the `jolt` binary it finds on `PATH` (override with
|
`jolt-deps` launches the `jolt` binary it finds on `PATH` (override with
|
||||||
|
|
@ -88,10 +92,27 @@ jolt-deps run -m myapp.main
|
||||||
dependency's own `deps.edn` are resolved too.
|
dependency's own `deps.edn` are resolved too.
|
||||||
- **local deps** — `{:local/root "../path"}`.
|
- **local deps** — `{:local/root "../path"}`.
|
||||||
- The project's own `:paths` (default `["src"]`) are included.
|
- The project's own `:paths` (default `["src"]`) are included.
|
||||||
|
- **aliases** — `:aliases {:dev {:extra-paths ["dev"] :extra-deps {…}
|
||||||
|
:main-opts ["-e" "…"]}}`, selected with `-A:dev` (or several: `-A:dev:test`).
|
||||||
|
`:extra-paths`/`:extra-deps` accumulate across selected aliases;
|
||||||
|
`:main-opts` is last-wins and runs via `-M:alias`.
|
||||||
|
- **user config** — a `deps.edn` under `$JOLT_CONFIG` (else
|
||||||
|
`$XDG_CONFIG_HOME/jolt`, else `~/.jolt`) merges beneath the project's, the
|
||||||
|
way `~/.clojure/deps.edn` does: `:deps`/`:aliases`/`:tasks` merge per key
|
||||||
|
with the project winning.
|
||||||
|
- **tasks** — `:tasks {clean "rm -rf target" test {:doc "run the suite"
|
||||||
|
:main-opts ["-e" "(run-tests)"]}}`. A string task is a shell command; a map
|
||||||
|
task runs jolt with its `:main-opts`. `jolt-deps tasks` lists, `jolt-deps
|
||||||
|
task NAME` runs.
|
||||||
|
|
||||||
Resolution reuses jpm's git fetch and cache (a dependency is cloned once into
|
Conflicts resolve the tools.deps way: resolution is breadth-first, so a
|
||||||
`jpm_tree/.cache` and reused). Resolved roots are cached on a hash of `deps.edn`,
|
top-level coordinate always beats a transitive one for the same lib, and
|
||||||
so an unchanged `deps.edn` doesn't re-fetch.
|
conflicting coordinates print a warning naming both.
|
||||||
|
|
||||||
|
Git clones land in a global, sha-immutable cache shared across projects —
|
||||||
|
`$JOLT_GITLIBS`, else `<config-dir>/gitlibs` (the `~/.gitlibs` model). The
|
||||||
|
resolved roots are cached per project in `.cpcache/jolt-deps.jdn`, keyed on a
|
||||||
|
hash of the project `deps.edn` + the user `deps.edn` + the selected aliases.
|
||||||
|
|
||||||
### What's not
|
### What's not
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,29 @@ syntax overlap for the `:deps`/`:paths` subset), then walks `:deps`:
|
||||||
- `:mvn/*` and anything else → ignored.
|
- `:mvn/*` and anything else → ignored.
|
||||||
|
|
||||||
Each resolved dependency contributes its own `:paths` (default `["src"]`) as
|
Each resolved dependency contributes its own `:paths` (default `["src"]`) as
|
||||||
source roots, and we recurse into its `deps.edn` for transitive deps. The result
|
source roots; the walk is **breadth-first** so every top-level coordinate
|
||||||
|
registers before any transitive one — a top-level pin always wins, matching
|
||||||
|
tools.deps, and a coordinate conflict warns on stderr naming both. The result
|
||||||
is a de-duplicated, ordered list of directories. `resolve-deps-cached` memoizes
|
is a de-duplicated, ordered list of directories. `resolve-deps-cached` memoizes
|
||||||
that list in the tree keyed on a hash of `deps.edn`, so an unchanged file doesn't
|
that list in the project-local `.cpcache/jolt-deps.jdn`, keyed on a hash of the
|
||||||
re-fetch. jpm is loaded lazily (`require`, not `import`) so it's pulled in only
|
project `deps.edn` + the user-level `deps.edn` + the selected aliases. jpm is
|
||||||
when resolving — never embedded in a built binary.
|
loaded lazily (`require`, not `import`) so it's pulled in only when resolving —
|
||||||
|
never embedded in a built binary.
|
||||||
|
|
||||||
|
Three tools.deps features are mirrored in reduced form. **Aliases**: `:aliases`
|
||||||
|
entries supply `:extra-paths`/`:extra-deps` (accumulate across the aliases
|
||||||
|
selected with `-A:a:b`) and `:main-opts` (last-wins, run with `-M:alias`).
|
||||||
|
**User config**: a `deps.edn` under `$JOLT_CONFIG` (else
|
||||||
|
`$XDG_CONFIG_HOME/jolt`, else `~/.jolt`) merges beneath the project file,
|
||||||
|
per key, project wins. **Tasks**: the honest subset of babashka's — a string
|
||||||
|
task is a shell command, a map task is `{:main-opts […] :doc "…"}`; bare
|
||||||
|
Clojure expressions aren't supported because the reader hands back parsed
|
||||||
|
data, and round-tripping it to source isn't worth the fragility.
|
||||||
|
|
||||||
|
Clones default to a global sha-immutable cache (`$JOLT_GITLIBS`, else
|
||||||
|
`<config-dir>/gitlibs`) shared across projects, the `tools.gitlibs`
|
||||||
|
`~/.gitlibs` model; per-project trees remain available by passing `tree`
|
||||||
|
explicitly.
|
||||||
|
|
||||||
The loader (`evaluator.janet/find-ns-file`) resolves a namespace by searching the
|
The loader (`evaluator.janet/find-ns-file`) resolves a namespace by searching the
|
||||||
context's `:source-paths` in order (the stdlib `src/jolt` first), trying `<ns>.clj`
|
context's `:source-paths` in order (the stdlib `src/jolt` first), trying `<ns>.clj`
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,35 @@
|
||||||
(each m [a b] (when (dictionary? m) (eachp [k v] m (put out k v))))
|
(each m [a b] (when (dictionary? m) (eachp [k v] m (put out k v))))
|
||||||
out)
|
out)
|
||||||
|
|
||||||
|
# Reader symbols carry position metadata, so any map keyed by SYMBOLS (deps
|
||||||
|
# libs, task names) must be re-keyed by name before merging or deduping.
|
||||||
|
(defn- sym-name [x]
|
||||||
|
(if (and (dictionary? x) (get x :name))
|
||||||
|
(if-let [ns (get x :ns)]
|
||||||
|
(string ns "/" (get x :name))
|
||||||
|
(get x :name))
|
||||||
|
(string x)))
|
||||||
|
|
||||||
|
(defn- merge-by-name [a b] # union of symbol-keyed dictionaries, b wins
|
||||||
|
(def out @{})
|
||||||
|
(each m [a b] (when (dictionary? m) (eachp [k v] m (put out (sym-name k) v))))
|
||||||
|
out)
|
||||||
|
|
||||||
|
(defn- mkdirs [p]
|
||||||
|
(def abs (string/has-prefix? "/" p))
|
||||||
|
(var acc nil)
|
||||||
|
(each seg (filter |(not= "" $) (string/split "/" p))
|
||||||
|
(set acc (cond (nil? acc) (if abs (string "/" seg) seg) (string acc "/" seg)))
|
||||||
|
(unless (os/stat acc) (os/mkdir acc))))
|
||||||
|
|
||||||
|
(defn- default-tree
|
||||||
|
"Where git clones land when no tree is given: $JOLT_GITLIBS, else
|
||||||
|
(config-dir)/gitlibs — a global, sha-immutable cache shared across projects
|
||||||
|
(the tools.gitlibs ~/.gitlibs model), not a per-project ./jpm_tree."
|
||||||
|
[]
|
||||||
|
(def g (os/getenv "JOLT_GITLIBS"))
|
||||||
|
(if (and g (> (length g) 0)) g (string (config-dir) "/gitlibs")))
|
||||||
|
|
||||||
(defn load-config
|
(defn load-config
|
||||||
"The project deps.edn merged over the user-level one (config-dir)/deps.edn.
|
"The project deps.edn merged over the user-level one (config-dir)/deps.edn.
|
||||||
tools.deps merge semantics: scalar keys and :paths replace (project wins),
|
tools.deps merge semantics: scalar keys and :paths replace (project wins),
|
||||||
|
|
@ -67,12 +96,16 @@
|
||||||
[deps-edn-path]
|
[deps-edn-path]
|
||||||
(def proj (read-edn deps-edn-path))
|
(def proj (read-edn deps-edn-path))
|
||||||
(def user (read-edn (string (config-dir) "/deps.edn")))
|
(def user (read-edn (string (config-dir) "/deps.edn")))
|
||||||
(cond
|
(if (and (nil? user) (nil? proj))
|
||||||
(nil? user) proj
|
nil
|
||||||
(nil? proj) user
|
# normalize even when only one file exists: :deps/:tasks come back keyed
|
||||||
(let [out (merge-per-key user proj)]
|
# by NAME (reader symbols carry position metadata and never compare equal)
|
||||||
(put out :deps (merge-per-key (get user :deps) (get proj :deps)))
|
(let [u (when (dictionary? user) user)
|
||||||
(put out :aliases (merge-per-key (get user :aliases) (get proj :aliases)))
|
p (when (dictionary? proj) proj)
|
||||||
|
out (merge-per-key u p)]
|
||||||
|
(put out :deps (merge-by-name (and u (get u :deps)) (and p (get p :deps))))
|
||||||
|
(put out :aliases (merge-per-key (and u (get u :aliases)) (and p (get p :aliases))))
|
||||||
|
(put out :tasks (merge-by-name (and u (get u :tasks)) (and p (get p :tasks))))
|
||||||
out)))
|
out)))
|
||||||
|
|
||||||
(defn combine-aliases
|
(defn combine-aliases
|
||||||
|
|
@ -107,22 +140,14 @@
|
||||||
`aliases` (keywords) pull :extra-paths/:extra-deps from the merged config's
|
`aliases` (keywords) pull :extra-paths/:extra-deps from the merged config's
|
||||||
:aliases. The user-level deps.edn (see load-config) merges under the project."
|
:aliases. The user-level deps.edn (see load-config) merges under the project."
|
||||||
[deps-edn-path &opt tree aliases]
|
[deps-edn-path &opt tree aliases]
|
||||||
(default tree (string (os/cwd) "/jpm_tree"))
|
(default tree (default-tree))
|
||||||
(os/mkdir tree)
|
(mkdirs tree)
|
||||||
(ensure-jpm-config tree)
|
(ensure-jpm-config tree)
|
||||||
(def edn (load-config deps-edn-path))
|
(def edn (load-config deps-edn-path))
|
||||||
(def extra (combine-aliases edn aliases))
|
(def extra (combine-aliases edn aliases))
|
||||||
(def roots @[])
|
(def roots @[])
|
||||||
(def seen @{}) # lib name -> chosen coordinate (for conflict reporting)
|
(def seen @{}) # lib name -> chosen coordinate (for conflict reporting)
|
||||||
(defn add-root [r] (unless (index-of r roots) (array/push roots r)))
|
(defn add-root [r] (unless (index-of r roots) (array/push roots r)))
|
||||||
# Reader symbols carry position metadata, so dedup/conflict keys must use the
|
|
||||||
# NAME, never (string lib) — two my/b symbols from different files differ.
|
|
||||||
(defn lib-name [lib]
|
|
||||||
(if (and (dictionary? lib) (get lib :name))
|
|
||||||
(if-let [ns (get lib :ns)]
|
|
||||||
(string ns "/" (get lib :name))
|
|
||||||
(get lib :name))
|
|
||||||
(string lib)))
|
|
||||||
(defn coord-str [spec]
|
(defn coord-str [spec]
|
||||||
(cond
|
(cond
|
||||||
(and (dictionary? spec) (get spec :local/root))
|
(and (dictionary? spec) (get spec :local/root))
|
||||||
|
|
@ -137,7 +162,7 @@
|
||||||
(deep= (get a :git/tag) (get b :git/tag))))
|
(deep= (get a :git/tag) (get b :git/tag))))
|
||||||
(def queue @[])
|
(def queue @[])
|
||||||
(defn discover [lib spec base-dir]
|
(defn discover [lib spec base-dir]
|
||||||
(def k (lib-name lib))
|
(def k (sym-name lib))
|
||||||
(if-let [prev (get seen k)]
|
(if-let [prev (get seen k)]
|
||||||
(unless (coord= prev spec)
|
(unless (coord= prev spec)
|
||||||
(eprintf "WARNING: %s: conflicting coordinates — using %s, ignoring %s"
|
(eprintf "WARNING: %s: conflicting coordinates — using %s, ignoring %s"
|
||||||
|
|
@ -158,13 +183,13 @@
|
||||||
# the project's own paths (+ alias extra paths) lead the roots
|
# the project's own paths (+ alias extra paths) lead the roots
|
||||||
(each r (src-roots (os/cwd) edn) (add-root r))
|
(each r (src-roots (os/cwd) edn) (add-root r))
|
||||||
(each pp (extra :extra-paths) (add-root (string (os/cwd) "/" pp)))
|
(each pp (extra :extra-paths) (add-root (string (os/cwd) "/" pp)))
|
||||||
# breadth-first: every top-level dep (incl. alias :extra-deps) registers
|
# breadth-first: every top-level dep registers before any transitive dep —
|
||||||
# before any transitive dep — so a top-level coordinate always wins,
|
# so a top-level coordinate always wins, matching tools.deps. Alias
|
||||||
# matching tools.deps
|
# :extra-deps go first: a selected alias's pin beats the project's.
|
||||||
(eachp [lib spec] (or (and (dictionary? edn) (get edn :deps)) {})
|
|
||||||
(discover lib spec (os/cwd)))
|
|
||||||
(eachp [lib spec] (extra :extra-deps)
|
(eachp [lib spec] (extra :extra-deps)
|
||||||
(discover lib spec (os/cwd)))
|
(discover lib spec (os/cwd)))
|
||||||
|
(eachp [lib spec] (or (and (dictionary? edn) (get edn :deps)) {})
|
||||||
|
(discover lib spec (os/cwd)))
|
||||||
(while (> (length queue) 0)
|
(while (> (length queue) 0)
|
||||||
(def [dep-edn dir] (get queue 0))
|
(def [dep-edn dir] (get queue 0))
|
||||||
(array/remove queue 0)
|
(array/remove queue 0)
|
||||||
|
|
@ -178,10 +203,12 @@
|
||||||
of the project deps.edn + the user deps.edn + the selected aliases, so an
|
of the project deps.edn + the user deps.edn + the selected aliases, so an
|
||||||
unchanged config resolves without re-fetching."
|
unchanged config resolves without re-fetching."
|
||||||
[deps-edn-path &opt tree aliases]
|
[deps-edn-path &opt tree aliases]
|
||||||
(default tree (string (os/cwd) "/jpm_tree"))
|
(default tree (default-tree))
|
||||||
(when (os/stat deps-edn-path)
|
(when (os/stat deps-edn-path)
|
||||||
(os/mkdir tree)
|
# the roots depend on the PROJECT (config + aliases), so their cache is
|
||||||
(def cache-file (string tree "/.jolt-deps-roots.jdn"))
|
# project-local like tools.deps' .cpcache; the clone tree stays global
|
||||||
|
(os/mkdir ".cpcache")
|
||||||
|
(def cache-file ".cpcache/jolt-deps.jdn")
|
||||||
(def user-path (string (config-dir) "/deps.edn"))
|
(def user-path (string (config-dir) "/deps.edn"))
|
||||||
(def h (hash [(slurp deps-edn-path)
|
(def h (hash [(slurp deps-edn-path)
|
||||||
(when (os/stat user-path) (slurp user-path))
|
(when (os/stat user-path) (slurp user-path))
|
||||||
|
|
@ -192,3 +219,31 @@
|
||||||
(let [roots (resolve-deps deps-edn-path tree aliases)]
|
(let [roots (resolve-deps deps-edn-path tree aliases)]
|
||||||
(spit cache-file (string/format "%j" {:hash h :roots roots}))
|
(spit cache-file (string/format "%j" {:hash h :roots roots}))
|
||||||
roots))))
|
roots))))
|
||||||
|
|
||||||
|
# --- :tasks (the honest subset of babashka's) ----------------------------------
|
||||||
|
# A STRING task is a shell command. A MAP task carries :main-opts (jolt args —
|
||||||
|
# `-e "(...)"` covers expression tasks) and an optional :doc. Babashka-style
|
||||||
|
# bare-expression tasks aren't supported: the reader hands us parsed data, and
|
||||||
|
# round-tripping it back to source isn't worth the fragility.
|
||||||
|
|
||||||
|
(defn tasks
|
||||||
|
"Sorted [name doc] pairs from the merged user+project :tasks."
|
||||||
|
[deps-edn-path]
|
||||||
|
(def m (get (load-config deps-edn-path) :tasks))
|
||||||
|
(def names (sort (keys (or m @{}))))
|
||||||
|
(map (fn [n]
|
||||||
|
(def v (get m n))
|
||||||
|
[n (when (dictionary? v) (get v :doc))])
|
||||||
|
names))
|
||||||
|
|
||||||
|
(defn task-spec
|
||||||
|
"What running task `name` means: {:type :shell :cmd s} or
|
||||||
|
{:type :jolt :argv [...]}; nil when undefined."
|
||||||
|
[deps-edn-path name]
|
||||||
|
(def v (get (or (get (load-config deps-edn-path) :tasks) @{}) name))
|
||||||
|
(cond
|
||||||
|
(nil? v) nil
|
||||||
|
(or (string? v) (buffer? v)) {:type :shell :cmd (string v)}
|
||||||
|
(and (dictionary? v) (get v :main-opts))
|
||||||
|
{:type :jolt :argv (tuple ;(map string (get v :main-opts)))}
|
||||||
|
(error (string "task " name ": use a shell string or {:main-opts [...]}"))))
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,10 @@
|
||||||
# jolt-deps -M:a[:b] [args] resolve with the aliases, then run jolt with
|
# jolt-deps -M:a[:b] [args] resolve with the aliases, then run jolt with
|
||||||
# the last alias's :main-opts ++ args
|
# the last alias's :main-opts ++ args
|
||||||
# jolt-deps uberscript OUT -m NS resolve, then bundle NS + deps into one .clj
|
# jolt-deps uberscript OUT -m NS resolve, then bundle NS + deps into one .clj
|
||||||
|
# jolt-deps tasks list :tasks (merged user+project deps.edn)
|
||||||
|
# jolt-deps task NAME [args] run a task: a string task is a shell command
|
||||||
|
# (args appended); a {:main-opts [...]} task
|
||||||
|
# runs jolt with those args ++ extra args
|
||||||
#
|
#
|
||||||
# -A:dev:test selects aliases (tools.deps style): their :extra-paths and
|
# -A:dev:test selects aliases (tools.deps style): their :extra-paths and
|
||||||
# :extra-deps join the resolution. A user-level deps.edn ($JOLT_CONFIG, else
|
# :extra-deps join the resolution. A user-level deps.edn ($JOLT_CONFIG, else
|
||||||
|
|
@ -60,6 +64,18 @@
|
||||||
(os/exit 1)))]
|
(os/exit 1)))]
|
||||||
(os/exit (exec-jolt als [;mo ;(tuple/slice argv 1)])))
|
(os/exit (exec-jolt als [;mo ;(tuple/slice argv 1)])))
|
||||||
(= cmd "path") (print (string/join (roots aliases) ":"))
|
(= cmd "path") (print (string/join (roots aliases) ":"))
|
||||||
|
(= cmd "tasks")
|
||||||
|
(each row (deps/tasks "deps.edn")
|
||||||
|
(print (row 0) (if (row 1) (string "\t" (row 1)) "")))
|
||||||
|
(= cmd "task")
|
||||||
|
(let [name (get argv 1)
|
||||||
|
spec (when name (deps/task-spec "deps.edn" name))]
|
||||||
|
(cond
|
||||||
|
(nil? name) (do (eprint "jolt-deps: task needs a name") (os/exit 1))
|
||||||
|
(nil? spec) (do (eprint "jolt-deps: no such task: " name) (os/exit 1))
|
||||||
|
(= :shell (spec :type))
|
||||||
|
(os/exit (os/execute ["sh" "-c" (string/join [(spec :cmd) ;(tuple/slice argv 2)] " ")] :p))
|
||||||
|
(os/exit (exec-jolt aliases [;(spec :argv) ;(tuple/slice argv 2)]))))
|
||||||
(= cmd "run") (os/exit (exec-jolt aliases (tuple/slice argv 1)))
|
(= cmd "run") (os/exit (exec-jolt aliases (tuple/slice argv 1)))
|
||||||
(= cmd "repl") (os/exit (exec-jolt aliases []))
|
(= cmd "repl") (os/exit (exec-jolt aliases []))
|
||||||
(= cmd "-e") (os/exit (exec-jolt aliases argv))
|
(= cmd "-e") (os/exit (exec-jolt aliases argv))
|
||||||
|
|
|
||||||
89
test/integration/deps-tasks-test.janet
Normal file
89
test/integration/deps-tasks-test.janet
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
# deps.edn :tasks (jolt-x4o) + the global gitlibs-style clone cache default
|
||||||
|
# (jolt-xkd). Tasks are the honest subset of babashka's: a STRING task is a
|
||||||
|
# shell command; a MAP task carries :main-opts (jolt args) and optional :doc.
|
||||||
|
# Local-only: no network, no jolt binary — the CLI wrappers stay thin.
|
||||||
|
|
||||||
|
(import ../../src/jolt/deps :as deps)
|
||||||
|
|
||||||
|
(def base (string (or (os/getenv "TMPDIR") "/tmp") "/jolt-deps-tasks-" (os/time)))
|
||||||
|
(defn rmrf [p]
|
||||||
|
(when (os/stat p)
|
||||||
|
(if (= :directory (os/stat p :mode))
|
||||||
|
(do (each e (os/dir p) (rmrf (string p "/" e))) (os/rmdir p))
|
||||||
|
(os/rm p))))
|
||||||
|
(rmrf base)
|
||||||
|
(defn mkdirs [p]
|
||||||
|
(def abs (string/has-prefix? "/" p))
|
||||||
|
(var acc nil)
|
||||||
|
(each seg (filter |(not= "" $) (string/split "/" p))
|
||||||
|
(set acc (cond (nil? acc) (if abs (string "/" seg) seg) (string acc "/" seg)))
|
||||||
|
(unless (os/stat acc) (os/mkdir acc))))
|
||||||
|
|
||||||
|
(each d ["proj/src" "config"] (mkdirs (string base "/" d)))
|
||||||
|
(spit (string base "/proj/deps.edn")
|
||||||
|
`{:paths ["src"]
|
||||||
|
:tasks {clean "rm -rf target"
|
||||||
|
test {:doc "run the suite" :main-opts ["-e" "(run-tests)"]}
|
||||||
|
fmt {:main-opts ["-e" "(fmt)"]}}}`)
|
||||||
|
# user-level task, and a project-shadowed name
|
||||||
|
(spit (string base "/config/deps.edn")
|
||||||
|
`{:tasks {lint "echo lint" clean "echo SHADOWED"}}`)
|
||||||
|
|
||||||
|
(var fails 0)
|
||||||
|
(defn check [label got expected]
|
||||||
|
(if (= got expected) (print " ok " label)
|
||||||
|
(do (++ fails) (printf " FAIL %s: want %q, got %q" label expected got))))
|
||||||
|
|
||||||
|
(os/cd (string base "/proj"))
|
||||||
|
(os/setenv "JOLT_CONFIG" (string base "/config"))
|
||||||
|
|
||||||
|
# --- task listing (merged user+project, sorted) ---------------------------------
|
||||||
|
(def listing (deps/tasks "deps.edn"))
|
||||||
|
(check "lists all task names"
|
||||||
|
(tuple ;(sort (map first listing))) ["clean" "fmt" "lint" "test"])
|
||||||
|
(check "doc shows in listing"
|
||||||
|
(truthy? (some |(and (= (first $) "test") (= (get $ 1) "run the suite")) listing))
|
||||||
|
true)
|
||||||
|
|
||||||
|
# --- task lookup ------------------------------------------------------------------
|
||||||
|
(check "string task is shell"
|
||||||
|
(deps/task-spec "deps.edn" "clean") {:type :shell :cmd "rm -rf target"})
|
||||||
|
(check "project task shadows user task"
|
||||||
|
(get (deps/task-spec "deps.edn" "clean") :cmd) "rm -rf target")
|
||||||
|
(check "map task is jolt argv"
|
||||||
|
(deps/task-spec "deps.edn" "test") {:type :jolt :argv ["-e" "(run-tests)"]})
|
||||||
|
(check "user-level task visible"
|
||||||
|
(deps/task-spec "deps.edn" "lint") {:type :shell :cmd "echo lint"})
|
||||||
|
(check "unknown task is nil"
|
||||||
|
(deps/task-spec "deps.edn" "nope") nil)
|
||||||
|
|
||||||
|
# --- tasks resolve with NO user config (regression: load-config skipped the
|
||||||
|
# name re-keying when only the project file existed) -----------------------------
|
||||||
|
(os/setenv "JOLT_CONFIG" (string base "/no-such-dir"))
|
||||||
|
(check "task works without user config"
|
||||||
|
(deps/task-spec "deps.edn" "clean") {:type :shell :cmd "rm -rf target"})
|
||||||
|
(check "listing works without user config"
|
||||||
|
(tuple ;(sort (map first (deps/tasks "deps.edn")))) ["clean" "fmt" "test"])
|
||||||
|
(os/setenv "JOLT_CONFIG" (string base "/config"))
|
||||||
|
|
||||||
|
# --- global clone-tree default (jolt-xkd) ----------------------------------------
|
||||||
|
# resolution with :local deps never clones, but the default tree dir must come
|
||||||
|
# from $JOLT_GITLIBS (else (config-dir)/gitlibs), not ./jpm_tree
|
||||||
|
(os/setenv "JOLT_GITLIBS" (string base "/deep/nested/gitlibs"))
|
||||||
|
(deps/resolve-deps "deps.edn")
|
||||||
|
(check "JOLT_GITLIBS dir created (parents too)"
|
||||||
|
(truthy? (os/stat (string base "/deep/nested/gitlibs"))) true)
|
||||||
|
(check "no per-project jpm_tree" (os/stat (string base "/proj/jpm_tree")) nil)
|
||||||
|
|
||||||
|
# roots cache is project-local .cpcache, not inside the clone tree
|
||||||
|
(deps/resolve-deps-cached "deps.edn")
|
||||||
|
(check "roots cache in ./.cpcache"
|
||||||
|
(truthy? (os/stat (string base "/proj/.cpcache/jolt-deps.jdn"))) true)
|
||||||
|
|
||||||
|
(os/setenv "JOLT_GITLIBS" "")
|
||||||
|
(os/cd "/")
|
||||||
|
(rmrf base)
|
||||||
|
|
||||||
|
(if (> fails 0)
|
||||||
|
(error (string "deps-tasks-test: " fails " failing check(s)"))
|
||||||
|
(print "\nAll deps-tasks tests passed!"))
|
||||||
Loading…
Add table
Add a link
Reference in a new issue