Dependency resolution now lives in the `jolt` CLI itself instead of a separate jolt-deps executable. `jolt` resolves a deps.edn into JOLT_PATH/JOLT_APP_PATHS in-process and dispatches the deps subcommands: jolt -M:alias [args] run the alias :main-opts jolt -A:alias CMD run CMD with the alias paths jolt run FILE resolve, then run FILE jolt path | tasks | task NAME A deps.edn in the working dir is auto-resolved for the runnable commands (repl/-m/-e/nrepl-server/FILE), so e.g. `jolt -M:nrepl` (or plain `jolt nrepl-server`) starts an nREPL with the project and its deps loaded. The runtime core stays deps-agnostic — it only reads JOLT_PATH. The resolver (deps.janet) is reached only from the CLI entry and loads jpm lazily, so a run with no deps.edn never touches it and an app baked from its own jolt/api entry never links it. resolve-deps-argv only resolves on an explicit deps command or when a deps.edn is present; help/version never do. jolt-deps stays as a thin deprecation shim that forwards to `jolt`, so existing scripts keep working. Docs (README, CLAUDE.md, building-and-deps, tools-deps) and the help text updated. Co-authored-by: Yogthos <yogthos@gmail.com>
98 lines
4.2 KiB
Text
98 lines
4.2 KiB
Text
# jolt-deps folded into jolt: the single `jolt` binary resolves a deps.edn into
|
|
# JOLT_PATH in-process and dispatches the deps subcommands (path/-M/run/tasks/
|
|
# task), and auto-resolves a deps.edn for runnable commands (repl/-m/-e/FILE).
|
|
# Drives the BUILT binary (baked overlay -> cwd-independent) from a fixture
|
|
# project dir so deps.edn in cwd is picked up. :local/root deps only — no
|
|
# network. Skips cleanly if build/jolt is absent (source-only run).
|
|
|
|
(def repo-root (os/cwd))
|
|
(def jolt (string repo-root "/build/jolt"))
|
|
|
|
(if-not (os/stat jolt)
|
|
(do (print "deps-merged-cli: SKIP (no build/jolt — run from source)") (os/exit 0)))
|
|
|
|
(def base (string (or (os/getenv "TMPDIR") "/tmp") "/jolt-merged-cli-" (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]
|
|
(var acc nil)
|
|
(each seg (filter |(not= "" $) (string/split "/" p))
|
|
(set acc (if (nil? acc) (string "/" seg) (string acc "/" seg)))
|
|
(unless (os/stat acc) (os/mkdir acc))))
|
|
|
|
# project depends on a local lib; an alias with :main-opts; an :extra-paths
|
|
# alias; a :tasks map (shell task)
|
|
(each d ["proj/src/app" "lib/src/mylib"] (mkdirs (string base "/" d)))
|
|
(spit (string base "/proj/deps.edn")
|
|
`{:paths ["src"]
|
|
:deps {my/lib {:local/root "../lib"}}
|
|
:aliases {:run {:main-opts ["-m" "app.core"]}
|
|
:dev {:extra-paths ["dev"]}}
|
|
:tasks {greet "echo hello-task"}}`)
|
|
(spit (string base "/lib/deps.edn") `{:paths ["src"]}`)
|
|
(spit (string base "/lib/src/mylib/core.clj") "(ns mylib.core)\n(defn answer [] 42)\n")
|
|
(spit (string base "/proj/src/app/core.clj")
|
|
"(ns app.core (:require [mylib.core :as m]))\n(defn -main [& args] (println \"MAIN\" (m/answer) (count args)))\n")
|
|
(spit (string base "/proj/script.clj")
|
|
"(require '[mylib.core :as m])\n(println \"SCRIPT\" (m/answer))\n")
|
|
|
|
# Run the built jolt from a given dir (cwd matters: deps.edn is read from cwd).
|
|
# Direct spawn (no shell) so arbitrary -e exprs need no quoting. Returns out+err.
|
|
(defn- run-in [dir & args]
|
|
(def prev (os/cwd))
|
|
(os/cd dir)
|
|
(def p (os/spawn [jolt ;args] :p {:out :pipe :err :pipe}))
|
|
(def out (:read (p :out) :all))
|
|
(def err (:read (p :err) :all))
|
|
(os/proc-wait p)
|
|
(os/cd prev)
|
|
(string (or out "") (or err "")))
|
|
|
|
(var fails 0)
|
|
(defn check [label got pred]
|
|
(if (pred got) (print " ok " label)
|
|
(do (++ fails) (printf " FAIL %s: got %q" label got))))
|
|
(defn- has [sub] (fn [s] (truthy? (string/find sub s))))
|
|
|
|
(def proj (string base "/proj"))
|
|
|
|
# `path` prints the resolved roots: project's own src + the local dep's src
|
|
(check "path includes project src" (run-in proj "path") (has "/proj/src"))
|
|
(check "path includes local dep src" (run-in proj "path") (has "/lib/src"))
|
|
|
|
# `-M:run` runs the alias :main-opts (-m app.core), requiring the local dep
|
|
(check "-M:run runs -main through resolved deps" (run-in proj "-M:run" "x" "y") (has "MAIN 42 2"))
|
|
|
|
# `run FILE` resolves deps then runs the file
|
|
(check "run FILE resolves deps" (run-in proj "run" "script.clj") (has "SCRIPT 42"))
|
|
|
|
# `tasks` lists the :tasks entries; `task NAME` runs a shell task
|
|
(check "tasks lists greet" (run-in proj "tasks") (has "greet"))
|
|
(check "task runs shell command" (run-in proj "task" "greet") (has "hello-task"))
|
|
|
|
# auto-resolve: a plain -e in a deps.edn dir can require the local dep
|
|
(check "-e auto-resolves deps.edn in cwd"
|
|
(run-in proj "-e" "(require '[mylib.core :as m]) (println (m/answer))")
|
|
(has "42"))
|
|
|
|
# -A:alias forces resolution (with that alias) for a runnable command
|
|
(check "-A:dev with -e resolves + runs"
|
|
(run-in proj "-A:dev" "-e" "(println (+ 1 2))")
|
|
(has "3"))
|
|
|
|
# no deps.edn: plain run is unaffected (resolver/jpm never touched)
|
|
(mkdirs (string base "/plain"))
|
|
(check "-e in a no-deps dir works unchanged"
|
|
(run-in (string base "/plain") "-e" "(println (* 6 7))")
|
|
(has "42"))
|
|
# help/version never trigger resolution
|
|
(check "version works in a deps dir" (run-in proj "--version") (has "jolt v"))
|
|
|
|
(rmrf base)
|
|
(if (> fails 0)
|
|
(do (printf "deps-merged-cli: %d FAILED" fails) (os/exit 1))
|
|
(print "deps-merged-cli: all cases passed"))
|