From d153f9852662f3975a928e115d306ec63988cc90 Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 24 Oct 2026 11:55:34 +0900 Subject: [PATCH 01/10] Route entrypoint commands without probing project code --- resources/entrypoint | 24 ++++--- test/docker_clojure/entrypoint_test.clj | 93 +++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 test/docker_clojure/entrypoint_test.clj diff --git a/resources/entrypoint b/resources/entrypoint index 3a50a540..df1ad4ee 100755 --- a/resources/entrypoint +++ b/resources/entrypoint @@ -4,18 +4,26 @@ set -eou pipefail entrypoint=@@entrypoint@@ -cmd=${1:-} - -# check if the first arg starts with a hyphen +# No arguments or a leading flag belong to the image's build tool. if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then exec "${entrypoint}" "$@" fi -if [[ -n "${cmd}" ]]; then - # see if help for the subcommand is successful - if "${entrypoint}" "${cmd}" --help >/dev/null 2>&1; then - exec "${entrypoint}" "$@" - fi +# Built-in Leiningen tasks and aliases take precedence over executables such as +# test and cp. Keep this Leiningen 2.13.0 list in sync when upgrading; never probe a +# command with --help, which can evaluate code from the current project. +if [ "${entrypoint}" = lein ]; then + case "$1" in + change|check|classpath|clean|compile|cp|deploy|deps|do|halp|help|install|jar|javac|\ + new|plugin|pom|readme|release|repl|retest|run|sample|search|show-profiles|test|\ + trampoline|tutorial|uberjar|update-in|upgrade|vcs|version|with-profile|with-profiles|überjar) + exec "${entrypoint}" "$@" + ;; + esac +elif [ -f "$1" ] && ! type -P "$1" >/dev/null; then + # Non-executable Clojure scripts run once, through clj. + exec "${entrypoint}" "$@" fi +# Other commands run directly. Use explicit lein for custom tasks and aliases. exec "$@" diff --git a/test/docker_clojure/entrypoint_test.clj b/test/docker_clojure/entrypoint_test.clj new file mode 100644 index 00000000..7d435c8c --- /dev/null +++ b/test/docker_clojure/entrypoint_test.clj @@ -0,0 +1,93 @@ +(ns docker-clojure.entrypoint-test + (:require [clojure.java.io :as io] + [clojure.java.shell :refer [sh]] + [clojure.string :as str] + [clojure.test :refer [deftest is testing]] + [docker-clojure.dockerfile :as df]) + (:import (java.nio.file Files) + (java.nio.file.attribute FileAttribute))) + +(defn- nul-fields [s] + (vec (butlast (str/split s #"\x00" -1)))) + +(defn- run-entrypoint [build-tool & args] + (let [dir (.toFile (Files/createTempDirectory "entrypoint test " (make-array FileAttribute 0))) + bin (io/file dir "bin") + log (io/file dir "calls")] + (try + (.mkdirs bin) + (df/shared-prereqs (str bin) {:build-tool build-tool}) + ;; Record every tool invocation, including probes with hidden output. + (doseq [tool ["lein" "clj"]] + (let [file (io/file bin tool)] + (spit file (str "#!/bin/sh\nprintf '%s\\0' '" tool "' \"$@\" >> \"$CALLS_LOG\"\n")) + (.setExecutable file true false))) + (spit (io/file dir "example.clj") "(println :example)") + ;; A local script must not take precedence over the executable on PATH. + (spit (io/file dir "echo") "(throw (Exception. \"unexpected project execution\"))") + (spit (io/file dir "launch") "#!/bin/sh\nprintf '%s\\n' external\n") + (.setExecutable (io/file dir "launch") true false) + (let [result (apply sh (str (io/file bin "entrypoint")) + (concat args [:dir (str dir) + :env {"PATH" (str bin ":/usr/bin:/bin") + "CALLS_LOG" (str log)}]))] + (assoc result :calls (if (.exists log) (nul-fields (slurp log)) []))) + (finally + (doseq [file (reverse (file-seq dir))] + (io/delete-file file)))))) + +(deftest external-commands-do-not-probe-test + (doseq [build-tool ["lein" "tools-deps" :docker-clojure.core/all]] + (testing (str build-tool " runs executables without launching a build tool") + (is (= {:exit 0 :out "hello\n" :err "" :calls []} + (run-entrypoint build-tool "echo" "hello"))) + (is (= {:exit 0 :out "external\n" :err "" :calls []} + (run-entrypoint build-tool "./launch"))) + (is (= {:exit 23 :out "" :err "failed\n" :calls []} + (run-entrypoint build-tool "/bin/sh" "-c" "echo failed >&2; exit 23")))))) + +(deftest defaults-and-flags-test + (doseq [[build-tool tool] [["lein" "lein"] ["tools-deps" "clj"] + [:docker-clojure.core/all "clj"]] + args [[] ["-h"] ["-M" "-e" "(+ 1 2)"]]] + (let [result (apply run-entrypoint build-tool args)] + (is (= 0 (:exit result))) + (is (= (into [tool] args) (:calls result)))))) + +(deftest lein-shorthand-and-explicit-invocation-test + (testing "built-in tasks and aliases keep precedence over executables" + (doseq [task ["repl" "test" "cp" "install" "help" "uberjar" "überjar"]] + (is (= ["lein" task] (:calls (run-entrypoint "lein" task)))))) + (testing "explicit commands and paths bypass shorthand routing" + (is (= ["lein" "my-alias"] (:calls (run-entrypoint "lein" "lein" "my-alias")))) + (is (= {:exit 0 :out "" :err "" :calls []} + (run-entrypoint "lein" "/bin/test" "1" "=" "1")))) + (testing "custom tasks use explicit Leiningen invocation" + (is (= ["lein" "my-task"] (:calls (run-entrypoint "lein" "lein" "my-task")))))) + +(deftest clojure-scripts-run-once-test + (doseq [build-tool ["tools-deps" :docker-clojure.core/all] + script ["example.clj" "./example.clj"]] + (let [result (run-entrypoint build-tool script "argument")] + (is (= 0 (:exit result))) + (is (= ["clj" script "argument"] (:calls result)))))) + +(deftest unknown-commands-do-not-launch-project-test + (doseq [build-tool ["lein" "tools-deps" :docker-clojure.core/all]] + (let [result (run-entrypoint build-tool "no-such-command")] + (is (= 127 (:exit result))) + (is (str/includes? (:err result) "no-such-command")) + (is (= [] (:calls result)))))) + +(deftest literal-arguments-test + (let [args ["" "two words" "*.clj" "line one\nline two" "$(exit 99)" "'quoted'"]] + (doseq [[build-tool command] [["lein" "test"] ["tools-deps" "-M"]]] + (let [result (apply run-entrypoint build-tool command args)] + (is (= 0 (:exit result))) + (is (= (into [(if (= build-tool "lein") "lein" "clj") command] args) + (:calls result))))) + (doseq [build-tool ["lein" "tools-deps"]] + (let [result (apply run-entrypoint build-tool "printf" "%s\\0" args)] + (is (= 0 (:exit result))) + (is (= args (nul-fields (:out result)))) + (is (= [] (:calls result))))))) From 6506e30f1803cd5f04e86aba784182b575f92b9f Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 24 Oct 2026 11:56:05 +0900 Subject: [PATCH 02/10] Document explicit invocation for custom Leiningen commands --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index 436bb516..c4de743c 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,41 @@ Java 21 tools-deps on Alpine: `clojure:temurin-21-tools-deps-alpine` or `clojure ## Examples +### Running commands + +With JDK 17+ images, you can pass built-in Leiningen tasks and aliases directly: + +```sh +docker run --rm clojure:lein test +docker run --rm clojure:lein cp +``` + +For custom tasks and aliases defined by your project or plugins, always use +explicit `lein ` invocation. The entrypoint only recognises +built-in names for shorthand invocation: + +```sh +docker run --rm -v "$PWD":/app -w /app clojure:lein lein my-alias +``` + +Executables such as `bash` and `git` run directly, without first loading your +Clojure project. If a custom task or alias shares an executable's name, the +executable takes precedence; use explicit `lein` to select the task or alias. +Earlier images probed Leiningen to discover these aliases, which could execute +project code before running an unrelated command. + +JDK 17+ tools-deps images accept Clojure flags and script files directly: + +```sh +docker run --rm clojure:tools-deps -M -e '(println (+ 1 2))' +docker run --rm -v "$PWD":/app -w /app clojure:tools-deps example.clj +``` + +With older JDK images, invoke `lein` or `clj` explicitly. + +To run an executable whose name is also a built-in Leiningen task or alias, +use its path (for example, `/bin/cp`) or Docker's `--entrypoint` option. + ### Interactive Shell Run an interactive shell from this image. From e27eb3b399f625ddfad758966f5a8e99b892b31a Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 24 Oct 2026 12:01:45 +0900 Subject: [PATCH 03/10] Shorten command usage examples --- README.md | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index c4de743c..c62ebd8d 100644 --- a/README.md +++ b/README.md @@ -107,38 +107,25 @@ Java 21 tools-deps on Alpine: `clojure:temurin-21-tools-deps-alpine` or `clojure ### Running commands -With JDK 17+ images, you can pass built-in Leiningen tasks and aliases directly: +For JDK 17+ images: ```sh +# Built-in Leiningen tasks and aliases docker run --rm clojure:lein test docker run --rm clojure:lein cp -``` - -For custom tasks and aliases defined by your project or plugins, always use -explicit `lein ` invocation. The entrypoint only recognises -built-in names for shorthand invocation: -```sh +# Custom tasks and aliases: always include lein docker run --rm -v "$PWD":/app -w /app clojure:lein lein my-alias -``` - -Executables such as `bash` and `git` run directly, without first loading your -Clojure project. If a custom task or alias shares an executable's name, the -executable takes precedence; use explicit `lein` to select the task or alias. -Earlier images probed Leiningen to discover these aliases, which could execute -project code before running an unrelated command. -JDK 17+ tools-deps images accept Clojure flags and script files directly: - -```sh +# Clojure flags and scripts docker run --rm clojure:tools-deps -M -e '(println (+ 1 2))' docker run --rm -v "$PWD":/app -w /app clojure:tools-deps example.clj ``` -With older JDK images, invoke `lein` or `clj` explicitly. +Programs such as `bash` and `git` run directly. Use `/bin/cp` to select the +executable instead of Leiningen's `cp` alias. -To run an executable whose name is also a built-in Leiningen task or alias, -use its path (for example, `/bin/cp`) or Docker's `--entrypoint` option. +With older JDK images, invoke `lein` or `clj` explicitly. ### Interactive Shell From d6cc94831fd55024dc2ca58999b788adb876907e Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 24 Oct 2026 12:04:37 +0900 Subject: [PATCH 04/10] Show explicit clj invocation for deps.edn aliases --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c62ebd8d..f4f91c7c 100644 --- a/README.md +++ b/README.md @@ -114,12 +114,15 @@ For JDK 17+ images: docker run --rm clojure:lein test docker run --rm clojure:lein cp -# Custom tasks and aliases: always include lein +# Custom Leiningen tasks and aliases: always include lein docker run --rm -v "$PWD":/app -w /app clojure:lein lein my-alias # Clojure flags and scripts docker run --rm clojure:tools-deps -M -e '(println (+ 1 2))' docker run --rm -v "$PWD":/app -w /app clojure:tools-deps example.clj + +# deps.edn aliases +docker run --rm -v "$PWD":/app -w /app clojure:tools-deps clj -M:my-alias ``` Programs such as `bash` and `git` run directly. Use `/bin/cp` to select the From e94a91ff32f12ba1356e44789bef9a870c5ee3fa Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 24 Oct 2026 12:07:53 +0900 Subject: [PATCH 05/10] Lead README examples with the interactive shell --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index f4f91c7c..dcf2aaf1 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,23 @@ Java 21 tools-deps on Alpine: `clojure:temurin-21-tools-deps-alpine` or `clojure ## Examples -### Running commands +### Interactive shell + +Run an interactive shell from this image. + +``` +docker run -ti clojure bash +``` + +Then within the shell, create a new Leiningen project and start a Clojure REPL. + +``` +lein new hello-world +cd hello-world +lein repl +``` + +### Running individual commands For JDK 17+ images: @@ -130,22 +146,6 @@ executable instead of Leiningen's `cp` alias. With older JDK images, invoke `lein` or `clj` explicitly. -### Interactive Shell - -Run an interactive shell from this image. - -``` -docker run -ti clojure bash -``` - -Then within the shell, create a new Leiningen project and start a Clojure REPL. - -``` -lein new hello-world -cd hello-world -lein repl -``` - ## Builds The Dockerfiles are generated by the `docker-clojure` Clojure app in this repo. From a156c5bd1d1f3364cb577f0eb49d1c1fbba2d3a9 Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 24 Oct 2026 12:14:31 +0900 Subject: [PATCH 06/10] Route lein static-classpath through the entrypoint Leiningen 2.11.0 added the static-classpath task, which the built-in task list missed. Co-Authored-By: Claude Opus 5.5 --- resources/entrypoint | 5 +++-- test/docker_clojure/entrypoint_test.clj | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/resources/entrypoint b/resources/entrypoint index df1ad4ee..e4e000c0 100755 --- a/resources/entrypoint +++ b/resources/entrypoint @@ -15,8 +15,9 @@ fi if [ "${entrypoint}" = lein ]; then case "$1" in change|check|classpath|clean|compile|cp|deploy|deps|do|halp|help|install|jar|javac|\ - new|plugin|pom|readme|release|repl|retest|run|sample|search|show-profiles|test|\ - trampoline|tutorial|uberjar|update-in|upgrade|vcs|version|with-profile|with-profiles|überjar) + new|plugin|pom|readme|release|repl|retest|run|sample|search|show-profiles|\ + static-classpath|test|trampoline|tutorial|uberjar|update-in|upgrade|vcs|version|\ + with-profile|with-profiles|überjar) exec "${entrypoint}" "$@" ;; esac diff --git a/test/docker_clojure/entrypoint_test.clj b/test/docker_clojure/entrypoint_test.clj index 7d435c8c..9c759284 100644 --- a/test/docker_clojure/entrypoint_test.clj +++ b/test/docker_clojure/entrypoint_test.clj @@ -56,7 +56,8 @@ (deftest lein-shorthand-and-explicit-invocation-test (testing "built-in tasks and aliases keep precedence over executables" - (doseq [task ["repl" "test" "cp" "install" "help" "uberjar" "überjar"]] + (doseq [task ["repl" "test" "cp" "install" "help" "static-classpath" + "uberjar" "überjar"]] (is (= ["lein" task] (:calls (run-entrypoint "lein" task)))))) (testing "explicit commands and paths bypass shorthand routing" (is (= ["lein" "my-alias"] (:calls (run-entrypoint "lein" "lein" "my-alias")))) From 68ff4b118e6476cf7e9a5617f47f25cf52940f1e Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 24 Oct 2026 12:18:40 +0900 Subject: [PATCH 07/10] Smoke test that lein images route every built-in lein command The entrypoint hardcodes Leiningen's tasks and aliases. Lein images with the entrypoint now list the tasks shipped in the Leiningen jar, plus its aliases, and check the installed entrypoint passes each one to lein, so a Leiningen upgrade that adds a task fails the image build. lein help is not a usable source: it omits static-classpath. Co-Authored-By: Claude Opus 5.5 --- resources/entrypoint | 5 ++-- src/docker_clojure/docker.clj | 52 ++++++++++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/resources/entrypoint b/resources/entrypoint index e4e000c0..d7954afc 100755 --- a/resources/entrypoint +++ b/resources/entrypoint @@ -10,8 +10,9 @@ if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then fi # Built-in Leiningen tasks and aliases take precedence over executables such as -# test and cp. Keep this Leiningen 2.13.0 list in sync when upgrading; never probe a -# command with --help, which can evaluate code from the current project. +# test and cp. Keep this Leiningen 2.13.0 list in sync when upgrading (the image +# smoke test checks it); never probe a command with --help, which can evaluate +# code from the current project. if [ "${entrypoint}" = lein ]; then case "$1" in change|check|classpath|clean|compile|cp|deploy|deps|do|halp|help|install|jar|javac|\ diff --git a/src/docker_clojure/docker.clj b/src/docker_clojure/docker.clj index bbdcd804..448a973f 100644 --- a/src/docker_clojure/docker.clj +++ b/src/docker_clojure/docker.clj @@ -12,14 +12,58 @@ (defn pull-image [image] (sh "docker" "pull" image)) +(def ^:private print-lein-commands + "Clojure form that prints every built-in Leiningen task and alias, one per + line. Tasks come from the namespaces in the Leiningen jar because `lein help` + omits tasks missing from its own stock list." + '(do + (require 'leiningen.core.main) + (let [jar (java.util.jar.JarFile. (System/getProperty "java.class.path")) + tasks (for [entry (enumeration-seq (.entries jar)) + :let [[_ file] (re-find #"^leiningen/([^/]+)\.clj$" + (.getName entry))] + :when file + :let [task (.replace file \_ \-) + task-ns (symbol (str "leiningen." task))] + :when (try (require task-ns) + (ns-resolve task-ns (symbol task)) + (catch java.io.FileNotFoundException _ nil))] + task) + out (java.io.PrintWriter. (java.io.OutputStreamWriter. System/out "UTF-8"))] + (doseq [command (concat tasks (keys leiningen.core.main/aliases))] + (.println out command)) + (.flush out)))) + +(def ^:private lein-routing-check + "Shell script that fails unless the entrypoint passes every built-in + Leiningen command to lein. Takes the `print-lein-commands` form as $1." + "set -e +commands=$(java -cp /usr/share/java/leiningen-*-standalone.jar clojure.main -e \"$1\") +stub=$(mktemp -d) +printf '%s\\n' '#!/bin/sh' 'echo \"lein $1\"' > \"$stub/lein\" +chmod +x \"$stub/lein\" +status=0 +while IFS= read -r command; do + routed=$(PATH=\"$stub:/usr/bin:/bin\" /usr/local/bin/entrypoint \"$command\" 2>/dev/null) || true + if [ \"$routed\" != \"lein $command\" ]; then + echo \"entrypoint does not pass $command to lein\" >&2 + status=1 + fi +done < (if (= ::core/all build-tool) + (vals cmds) + [(get cmds build-tool)]) + (and (= "lein" build-tool) (df/entrypoint? variant)) + (conj ["sh" "-c" lein-routing-check "sh" (pr-str print-lein-commands)])))) (defn smoke-test "Runs the variant's smoke test commands in `image-tag`. Returns true if they From e2225b71e9a2689696d1a5efc7bcfa96ba46ebc3 Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 24 Oct 2026 12:29:36 +0900 Subject: [PATCH 08/10] Describe both smoke test checks in smoke-test-cmds Co-Authored-By: Claude Opus 5.5 --- src/docker_clojure/docker.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/docker_clojure/docker.clj b/src/docker_clojure/docker.clj index 448a973f..16b352d5 100644 --- a/src/docker_clojure/docker.clj +++ b/src/docker_clojure/docker.clj @@ -55,7 +55,9 @@ EOF exit $status") (defn smoke-test-cmds - "Commands to run in a freshly built image to check its build tool(s) work." + "Commands to run in a freshly built image; each must exit 0. They check the + build tool(s) start and, in lein images with the entrypoint, that the + entrypoint passes every built-in lein command to lein." [{:keys [build-tool] :as variant}] (let [cmds {"lein" ["lein" "version"] "tools-deps" ["clojure" "-M" "-e" "(clojure-version)"]}] From 6b6cf985e85c6ae3b83971e6b99a122c92aebeb2 Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 24 Oct 2026 12:32:58 +0900 Subject: [PATCH 09/10] Simplify smoke test docstrings --- src/docker_clojure/docker.clj | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/docker_clojure/docker.clj b/src/docker_clojure/docker.clj index 16b352d5..53e945dd 100644 --- a/src/docker_clojure/docker.clj +++ b/src/docker_clojure/docker.clj @@ -13,9 +13,8 @@ (sh "docker" "pull" image)) (def ^:private print-lein-commands - "Clojure form that prints every built-in Leiningen task and alias, one per - line. Tasks come from the namespaces in the Leiningen jar because `lein help` - omits tasks missing from its own stock list." + "Prints built-in Leiningen tasks and aliases, one per line. + Includes tasks missing from `lein help`, e.g. `static-classpath`." '(do (require 'leiningen.core.main) (let [jar (java.util.jar.JarFile. (System/getProperty "java.class.path")) @@ -35,8 +34,8 @@ (.flush out)))) (def ^:private lein-routing-check - "Shell script that fails unless the entrypoint passes every built-in - Leiningen command to lein. Takes the `print-lein-commands` form as $1." + "Checks every built-in command routes to lein, e.g. `test` -> `lein test`. + $1 is the `print-lein-commands` form." "set -e commands=$(java -cp /usr/share/java/leiningen-*-standalone.jar clojure.main -e \"$1\") stub=$(mktemp -d) @@ -55,9 +54,9 @@ EOF exit $status") (defn smoke-test-cmds - "Commands to run in a freshly built image; each must exit 0. They check the - build tool(s) start and, in lein images with the entrypoint, that the - entrypoint passes every built-in lein command to lein." + "Returns commands to check build tools start. Each must exit 0. + Lein images with an entrypoint also check every built-in command routes + to lein, e.g. `test` -> `lein test`." [{:keys [build-tool] :as variant}] (let [cmds {"lein" ["lein" "version"] "tools-deps" ["clojure" "-M" "-e" "(clojure-version)"]}] From 563ccab0e71551b1ee0515b9c5df809a8a36c1fd Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 24 Oct 2026 12:50:07 +0900 Subject: [PATCH 10/10] Move lein routing check into a shell resource --- resources/lein-routing-check.sh | 20 ++++++++++++++++++++ src/docker_clojure/docker.clj | 20 +++----------------- 2 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 resources/lein-routing-check.sh diff --git a/resources/lein-routing-check.sh b/resources/lein-routing-check.sh new file mode 100644 index 00000000..5493f0b5 --- /dev/null +++ b/resources/lein-routing-check.sh @@ -0,0 +1,20 @@ +set -e + +commands=$(java -cp /usr/share/java/leiningen-*-standalone.jar clojure.main -e "$1") + +stub=$(mktemp -d) +printf '%s\n' '#!/bin/sh' 'echo "lein $1"' > "$stub/lein" +chmod +x "$stub/lein" + +status=0 +while IFS= read -r command; do + routed=$(PATH="$stub:/usr/bin:/bin" /usr/local/bin/entrypoint "$command" 2>/dev/null) || true + if [ "$routed" != "lein $command" ]; then + echo "entrypoint does not pass $command to lein" >&2 + status=1 + fi +done < `lein test`. $1 is the `print-lein-commands` form." - "set -e -commands=$(java -cp /usr/share/java/leiningen-*-standalone.jar clojure.main -e \"$1\") -stub=$(mktemp -d) -printf '%s\\n' '#!/bin/sh' 'echo \"lein $1\"' > \"$stub/lein\" -chmod +x \"$stub/lein\" -status=0 -while IFS= read -r command; do - routed=$(PATH=\"$stub:/usr/bin:/bin\" /usr/local/bin/entrypoint \"$command\" 2>/dev/null) || true - if [ \"$routed\" != \"lein $command\" ]; then - echo \"entrypoint does not pass $command to lein\" >&2 - status=1 - fi -done <