Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{org.clojure/clojure {:mvn/version "1.12.4"}
org.clojure/math.combinatorics {:mvn/version "0.3.2"}
org.clojure/core.async {:mvn/version "1.9.865"}
selmer/selmer {:mvn/version "1.12.61"}
com.gfredericks/test.chuck {:git/url "https://github.com/gfredericks/test.chuck"
:git/sha "ab5c11b013d3526e587dd53a860fa651b3e8a5a7"}}

Expand Down
12 changes: 12 additions & 0 deletions resources/templates/Dockerfile.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM {{from}}

{% if copy-java %}ENV JAVA_HOME=/opt/java/openjdk
COPY --from=eclipse-temurin:{{jdk-version}} $JAVA_HOME $JAVA_HOME
ENV PATH="${JAVA_HOME}/bin:${PATH}"

{% endif %}{{body}}

{% if entrypoint %}COPY entrypoint /usr/local/bin/entrypoint

ENTRYPOINT ["entrypoint"]
{% endif %}{{cmd}}
35 changes: 35 additions & 0 deletions resources/templates/lein.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
ENV LEIN_VERSION={{lein-version}}
ENV LEIN_INSTALL=/usr/local/bin/

WORKDIR /tmp

# Download the whole repo as an archive
RUN set -eux; \
{% for dep in install-deps %}{{dep}} && \
{% endfor %}mkdir -p $LEIN_INSTALL && \
wget -q https://codeberg.org/leiningen/leiningen/raw/tag/$LEIN_VERSION/bin/lein-pkg && \
echo "Comparing lein-pkg checksum ..." && \
sha256sum lein-pkg && \
echo "{{lein-pkg-hash}} *lein-pkg" | sha256sum -c - && \
mv lein-pkg $LEIN_INSTALL/lein && \
chmod 0755 $LEIN_INSTALL/lein && \
export GNUPGHOME="$(mktemp -d)" && \
export FILENAME_EXT=jar && \
gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys {{gpg-key}} && \
wget -q https://codeberg.org/leiningen/leiningen/releases/download/$LEIN_VERSION/leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT && \
wget -q https://codeberg.org/leiningen/leiningen/releases/download/$LEIN_VERSION/leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT.asc && \
echo "Verifying file PGP signature..." && \
gpg --batch --verify leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT.asc leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT && \
gpgconf --kill all && \
rm -rf "$GNUPGHOME" leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT.asc && \
mkdir -p /usr/share/java && \
mkdir -p /root/.lein && \
mv leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT /usr/share/java/leiningen-$LEIN_VERSION-standalone.jar{% for dep in uninstall-deps %} && \
{{dep}}{% endfor %}

ENV PATH=$PATH:$LEIN_INSTALL
ENV LEIN_ROOT 1

# Install clojure {{clojure-version}} so users don't have to download it every time
RUN echo '(defproject dummy "" :dependencies [[org.clojure/clojure "{{clojure-version}}"]])' > project.clj \
&& lein deps && rm project.clj
19 changes: 19 additions & 0 deletions resources/templates/tools-deps.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
ENV CLOJURE_VERSION={{clojure-version}}

WORKDIR /tmp

RUN \
{% for dep in install-deps %}{{dep}} && \
{% endfor %}curl -fsSLO https://download.clojure.org/install/linux-install-$CLOJURE_VERSION.sh && \
sha256sum linux-install-$CLOJURE_VERSION.sh && \
echo "{{install-hash}} *linux-install-$CLOJURE_VERSION.sh" | sha256sum -c - && \
chmod +x linux-install-$CLOJURE_VERSION.sh && \
./linux-install-$CLOJURE_VERSION.sh && \
rm linux-install-$CLOJURE_VERSION.sh && \
clojure -e "(clojure-version)"{% for dep in uninstall-deps %} && \
{{dep}}{% endfor %}

# Docker bug makes rlwrap crash w/o short sleep first
# Bug: https://github.com/moby/moby/issues/28009
# As of 2021-09-10 this bug still exists, despite that issue being closed
COPY rlwrap.retry /usr/local/bin/rlwrap
81 changes: 48 additions & 33 deletions src/docker_clojure/dockerfile.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[clojure.string :as str]
[docker-clojure.dockerfile.lein :as lein]
[docker-clojure.dockerfile.tools-deps :as tools-deps]
[docker-clojure.dockerfile.shared :refer [copy-resource-file! entrypoint]]
[docker-clojure.dockerfile.shared :refer [copy-resource-file! render-template]]
[docker-clojure.log :refer [log]]))

(defn build-dir [{:keys [base-image-tag jdk-version build-tool]}]
Expand All @@ -19,40 +19,55 @@
(defn all-prereqs [dir variant]
(tools-deps/prereqs dir variant))

(defn all-contents [installer-hashes variant]
(concat
["" "### INSTALL LEIN ###"]
(lein/install
installer-hashes
(assoc variant :build-tool-version
(get-in variant [:build-tool-versions "lein"])))
["" "### INSTALL TOOLS-DEPS ###"]
(tools-deps/install
installer-hashes
(assoc variant :build-tool-version
(get-in variant [:build-tool-versions "tools-deps"])))
[""]
(entrypoint variant)
["" "CMD [\"-M\", \"--repl\"]"]))
(defn copy-java?
"Debian variants copy the JDK in from an eclipse-temurin image; the temurin
base images already have it."
[{:keys [distro]}]
(contains? #{:debian :debian-slim} (-> distro namespace keyword)))

(defn copy-java-from-temurin-contents
[{:keys [jdk-version] :as _variant}]
["ENV JAVA_HOME=/opt/java/openjdk"
(str "COPY --from=eclipse-temurin:" jdk-version " $JAVA_HOME $JAVA_HOME")
"ENV PATH=\"${JAVA_HOME}/bin:${PATH}\""
""])
(defn entrypoint?
"JDK 16+ ships a `repl` so we install our wrapper entrypoint for it."
[{:keys [jdk-version]}]
(>= jdk-version 16))

(defn contents [installer-hashes {:keys [build-tool distro] :as variant}]
(str/join "\n"
(concat [(format "FROM %s" (:base-image-tag variant))
""]
(case (-> distro namespace keyword)
(:debian :debian-slim) (copy-java-from-temurin-contents variant)
[])
(case build-tool
:docker-clojure.core/all (all-contents installer-hashes variant)
"lein" (lein/contents installer-hashes variant)
"tools-deps" (tools-deps/contents installer-hashes variant)))))
(defn for-build-tool
"Return `variant` with `:build-tool-version` set to the given tool's version.
Single-build-tool variants already carry it; the combined `latest` image
pulls each tool's version from `:build-tool-versions`."
[variant build-tool]
(cond-> variant
(nil? (:build-tool-version variant))
(assoc :build-tool-version (get-in variant [:build-tool-versions build-tool]))))

(defn body
"The build-tool install section(s) that go between the base image setup and
the entrypoint. The combined `latest` image stacks both behind headers."
[installer-hashes {:keys [build-tool] :as variant}]
(case build-tool
"lein" (lein/install installer-hashes variant)
"tools-deps" (tools-deps/install installer-hashes variant)
:docker-clojure.core/all
(str "\n### INSTALL LEIN ###\n"
(lein/install installer-hashes (for-build-tool variant "lein"))
"\n\n### INSTALL TOOLS-DEPS ###\n"
(tools-deps/install installer-hashes (for-build-tool variant "tools-deps")))))

(defn command
[{:keys [build-tool] :as variant}]
(case build-tool
"lein" (lein/command variant)
"tools-deps" (tools-deps/command variant)
:docker-clojure.core/all "CMD [\"-M\", \"--repl\"]"))

(defn contents [installer-hashes variant]
(render-template
"templates/Dockerfile.tmpl"
{:from (:base-image-tag variant)
:copy-java (copy-java? variant)
:jdk-version (:jdk-version variant)
:body (body installer-hashes variant)
:entrypoint (entrypoint? variant)
:cmd (command variant)}))

(defn shared-prereqs [dir {:keys [build-tool]}]
(let [entrypoint (case build-tool
Expand Down
64 changes: 15 additions & 49 deletions src/docker_clojure/dockerfile/lein.clj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(ns docker-clojure.dockerfile.lein
(:require [clojure.string :as str]
[docker-clojure.dockerfile.shared
:refer [concat-commands entrypoint install-distro-deps
:refer [install-distro-deps render-template
uninstall-distro-build-deps]]))

(defn prereqs [_ _] nil)
Expand All @@ -20,6 +20,10 @@

(def uninstall-build-deps (partial uninstall-distro-build-deps distro-deps))

;; Clojure version pre-installed into lein images so users don't download it on
;; first use.
(def ^:const bundled-clojure-version "1.12.1")

(def ^:const old-key "6A2D483DB59437EBB97D09B1040193357D0606ED")
(def ^:const new-key "9D13D9426A0814B3373CF5E3D8A8243577A7859F")

Expand All @@ -32,54 +36,16 @@
:else old-key)))

(defn install [installer-hashes {:keys [build-tool-version] :as variant}]
(let [install-dep-cmds (install-deps variant)
uninstall-dep-cmds (uninstall-build-deps variant)]
(-> [(format "ENV LEIN_VERSION=%s" build-tool-version)
"ENV LEIN_INSTALL=/usr/local/bin/"
""
"WORKDIR /tmp"
""
"# Download the whole repo as an archive"
"RUN set -eux; \\"]
(concat-commands install-dep-cmds)
(concat-commands
["mkdir -p $LEIN_INSTALL"
"wget -q https://codeberg.org/leiningen/leiningen/raw/tag/$LEIN_VERSION/bin/lein-pkg"
"echo \"Comparing lein-pkg checksum ...\""
"sha256sum lein-pkg"
(str "echo \"" (get-in installer-hashes ["lein" build-tool-version]) " *lein-pkg\" | sha256sum -c -")
"mv lein-pkg $LEIN_INSTALL/lein"
"chmod 0755 $LEIN_INSTALL/lein"
"export GNUPGHOME=\"$(mktemp -d)\""
"export FILENAME_EXT=jar" ; used to be zip but hopefully it's always jar now?
(str "gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys "
(gpg-key build-tool-version))
"wget -q https://codeberg.org/leiningen/leiningen/releases/download/$LEIN_VERSION/leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT"
"wget -q https://codeberg.org/leiningen/leiningen/releases/download/$LEIN_VERSION/leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT.asc"
"echo \"Verifying file PGP signature...\""
"gpg --batch --verify leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT.asc leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT"
"gpgconf --kill all"
"rm -rf \"$GNUPGHOME\" leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT.asc"
"mkdir -p /usr/share/java"
"mkdir -p /root/.lein"
"mv leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT /usr/share/java/leiningen-$LEIN_VERSION-standalone.jar"]
(empty? uninstall-dep-cmds))
(concat-commands uninstall-dep-cmds :end)
(concat
[""
"ENV PATH=$PATH:$LEIN_INSTALL"
"ENV LEIN_ROOT 1"
""
"# Install clojure 1.12.1 so users don't have to download it every time"
"RUN echo '(defproject dummy \"\" :dependencies [[org.clojure/clojure \"1.12.1\"]])' > project.clj \\"
" && lein deps && rm project.clj"])

(->> (remove nil?)))))
(render-template
"templates/lein.tmpl"
{:lein-version build-tool-version
:clojure-version bundled-clojure-version
:lein-pkg-hash (get-in installer-hashes ["lein" build-tool-version])
:gpg-key (gpg-key build-tool-version)
:install-deps (install-deps variant)
:uninstall-deps (uninstall-build-deps variant)}))

(defn command [{:keys [jdk-version]}]
(if (>= jdk-version 16)
["CMD [\"repl\"]"]
["CMD [\"lein\", \"repl\"]"]))

(defn contents [installer-hashes variant]
(concat (install installer-hashes variant) [""] (entrypoint variant) (command variant)))
"CMD [\"repl\"]"
"CMD [\"lein\", \"repl\"]"))
34 changes: 13 additions & 21 deletions src/docker_clojure/dockerfile/shared.clj
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
(ns docker-clojure.dockerfile.shared
(:require [clojure.string :as str]
[clojure.java.io :as io]))
(:require [clojure.java.io :as io]
[clojure.string :as str]
[selmer.parser :as selmer]
[selmer.util :as selmer-util]))

(defn concat-commands [base cmds & [end?]]
(let [commands (if end?
(butlast cmds)
cmds)]
(concat base
(map #(str % " && \\")
commands)
(when end? [(last cmds)]))))
;; Dockerfiles aren't HTML, so don't let Selmer escape any of the values we
;; interpolate (quotes, brackets, etc. must pass through verbatim).
(selmer-util/turn-off-escaping!)

(defn render-template
"Render the Selmer template at resource path `tmpl` with `context`, trimming
any trailing newline so callers control the surrounding whitespace."
[tmpl context]
(-> tmpl io/resource slurp (selmer/render context) str/trim-newline))

(defn get-deps [type distro-deps distro]
(some->> distro namespace keyword (get distro-deps) type))
Expand Down Expand Up @@ -61,14 +64,3 @@
dest (io/file build-dir filename)]
(->> src slurp contents-processor (spit dest))
(file-processor dest))))

(defn entrypoint
"This is the same for every build-tool so far, so it's in here. If that
changes move it into the build-tool-specific namespaces (or future protocol)."
[{:keys [jdk-version]}]
(if (>= jdk-version 16)
(concat
["COPY entrypoint /usr/local/bin/entrypoint"]
[""]
["ENTRYPOINT [\"entrypoint\"]"])
nil))
45 changes: 10 additions & 35 deletions src/docker_clojure/dockerfile/tools_deps.clj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(ns docker-clojure.dockerfile.tools-deps
(:require [docker-clojure.dockerfile.shared
:refer [concat-commands copy-resource-file! entrypoint
install-distro-deps uninstall-distro-build-deps]]))
:refer [copy-resource-file! install-distro-deps render-template
uninstall-distro-build-deps]]))

(defn prereqs [dir _variant]
(copy-resource-file! dir "rlwrap.retry" identity
Expand All @@ -23,40 +23,15 @@

(def uninstall-build-deps (partial uninstall-distro-build-deps distro-deps))

(def docker-bug-notice
["# Docker bug makes rlwrap crash w/o short sleep first"
"# Bug: https://github.com/moby/moby/issues/28009"
"# As of 2021-09-10 this bug still exists, despite that issue being closed"])

(defn install [installer-hashes {:keys [build-tool-version] :as variant}]
(let [install-dep-cmds (install-deps variant)
uninstall-dep-cmds (uninstall-build-deps variant)]
(-> [(format "ENV CLOJURE_VERSION=%s" build-tool-version)
""
"WORKDIR /tmp"
""
"RUN \\"]
(concat-commands install-dep-cmds)
(concat-commands
["curl -fsSLO https://download.clojure.org/install/linux-install-$CLOJURE_VERSION.sh"
"sha256sum linux-install-$CLOJURE_VERSION.sh"
(str "echo \"" (get-in installer-hashes ["tools-deps" build-tool-version]) " *linux-install-$CLOJURE_VERSION.sh\" | sha256sum -c -")
"chmod +x linux-install-$CLOJURE_VERSION.sh"
"./linux-install-$CLOJURE_VERSION.sh"
"rm linux-install-$CLOJURE_VERSION.sh"
"clojure -e \"(clojure-version)\""] (empty? uninstall-dep-cmds))
(concat-commands uninstall-dep-cmds :end)
(concat [""] docker-bug-notice
["COPY rlwrap.retry /usr/local/bin/rlwrap"])
(->> (remove nil?)))))
(render-template
"templates/tools-deps.tmpl"
{:clojure-version build-tool-version
:install-hash (get-in installer-hashes ["tools-deps" build-tool-version])
:install-deps (install-deps variant)
:uninstall-deps (uninstall-build-deps variant)}))

(defn command [{:keys [jdk-version]}]
(if (>= jdk-version 16)
["CMD [\"-M\", \"--repl\"]"]
[(str "CMD [\"clj\"]")]))

(defn contents [installer-hashes variant]
(concat (install installer-hashes variant)
[""]
(entrypoint variant)
(command variant)))
"CMD [\"-M\", \"--repl\"]"
"CMD [\"clj\"]"))
1 change: 0 additions & 1 deletion target/debian-bookworm-25/latest/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,4 @@ COPY rlwrap.retry /usr/local/bin/rlwrap
COPY entrypoint /usr/local/bin/entrypoint

ENTRYPOINT ["entrypoint"]

CMD ["-M", "--repl"]
12 changes: 7 additions & 5 deletions test/docker_clojure/dockerfile_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,21 @@
:jdk-version 11})
"LABEL "))))
(testing "lein variant includes lein-specific contents"
(with-redefs [lein/contents (constantly ["leiningen vs. the ants"])]
(with-redefs [lein/install (constantly "leiningen vs. the ants")]
(is (str/includes? (contents cfg/installer-hashes
{:base-image-tag "base:foo"
:distro :distro/distro
:build-tool "lein"
:maintainer "Me Myself"})
:maintainer "Me Myself"
:jdk-version 11})
"leiningen vs. the ants"))))
(testing "tools-deps variant includes tools-deps-specific contents"
(with-redefs [tools-deps/contents (constantly
["Tools Deps is not a build tool"])]
(with-redefs [tools-deps/install (constantly
"Tools Deps is not a build tool")]
(is (str/includes? (contents cfg/installer-hashes
{:base-image-tag "base:foo"
:distro :distro/distro
:build-tool "tools-deps"
:maintainer "Me Myself"})
:maintainer "Me Myself"
:jdk-version 11})
"Tools Deps is not a build tool")))))
Loading