Skip to content

Commit ff90ab2

Browse files
committed
Update Leiningen to 2.13.0 and generate Dockerfiles from Selmer templates
Source, template, config, and test changes. Generated Dockerfile output is committed separately.
1 parent 2029b96 commit ff90ab2

10 files changed

Lines changed: 173 additions & 164 deletions

File tree

deps.edn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{org.clojure/clojure {:mvn/version "1.12.4"}
33
org.clojure/math.combinatorics {:mvn/version "0.3.2"}
44
org.clojure/core.async {:mvn/version "1.9.865"}
5+
selmer/selmer {:mvn/version "1.12.61"}
56
com.gfredericks/test.chuck {:git/url "https://github.com/gfredericks/test.chuck"
67
:git/sha "ab5c11b013d3526e587dd53a860fa651b3e8a5a7"}}
78

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM {{from}}
2+
3+
{% if copy-java %}ENV JAVA_HOME=/opt/java/openjdk
4+
COPY --from=eclipse-temurin:{{jdk-version}} $JAVA_HOME $JAVA_HOME
5+
ENV PATH="${JAVA_HOME}/bin:${PATH}"
6+
7+
{% endif %}{{body}}
8+
9+
{% if entrypoint %}COPY entrypoint /usr/local/bin/entrypoint
10+
11+
ENTRYPOINT ["entrypoint"]
12+
{% endif %}{{cmd}}

resources/templates/lein.tmpl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
ENV LEIN_VERSION={{lein-version}}
2+
ENV LEIN_INSTALL=/usr/local/bin/
3+
4+
WORKDIR /tmp
5+
6+
# No standalone uberjar is published for this version, so build it from source.
7+
# Verify Leiningen's GPG-signed release tag, then bootstrap leiningen-core's
8+
# dependencies with Maven and run `lein uberjar`.
9+
RUN set -eux; \
10+
{% for dep in install-deps %}{{dep}} && \
11+
{% endfor %}export GNUPGHOME="$(mktemp -d)" && \
12+
export LEIN_ROOT=1 && \
13+
mkdir -p $LEIN_INSTALL /usr/share/java /root/.lein && \
14+
gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys {{gpg-key}} && \
15+
git clone --depth 1 --branch $LEIN_VERSION https://codeberg.org/leiningen/leiningen.git && \
16+
cd leiningen && \
17+
git verify-tag $LEIN_VERSION && \
18+
( cd leiningen-core && mvn -B -q -DskipTests install && mvn -B -q dependency:build-classpath -Dmdep.outputFile=.lein-bootstrap ) && \
19+
bin/lein uberjar && \
20+
install -m 0644 target/leiningen-$LEIN_VERSION-standalone.jar /usr/share/java/leiningen-$LEIN_VERSION-standalone.jar && \
21+
install -m 0755 bin/lein-pkg $LEIN_INSTALL/lein && \
22+
gpgconf --kill all && \
23+
cd /tmp && \
24+
rm -rf /tmp/leiningen /root/.m2 "$GNUPGHOME"{% for dep in uninstall-deps %} && \
25+
{{dep}}{% endfor %}
26+
27+
ENV PATH=$PATH:$LEIN_INSTALL
28+
ENV LEIN_ROOT=1
29+
30+
# Install clojure {{clojure-version}} so users don't have to download it every time
31+
RUN echo '(defproject dummy "" :dependencies [[org.clojure/clojure "{{clojure-version}}"]])' > project.clj \
32+
&& lein deps && rm project.clj
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
ENV CLOJURE_VERSION={{clojure-version}}
2+
3+
WORKDIR /tmp
4+
5+
RUN \
6+
{% for dep in install-deps %}{{dep}} && \
7+
{% endfor %}curl -fsSLO https://download.clojure.org/install/linux-install-$CLOJURE_VERSION.sh && \
8+
sha256sum linux-install-$CLOJURE_VERSION.sh && \
9+
echo "{{install-hash}} *linux-install-$CLOJURE_VERSION.sh" | sha256sum -c - && \
10+
chmod +x linux-install-$CLOJURE_VERSION.sh && \
11+
./linux-install-$CLOJURE_VERSION.sh && \
12+
rm linux-install-$CLOJURE_VERSION.sh && \
13+
clojure -e "(clojure-version)"{% for dep in uninstall-deps %} && \
14+
{{dep}}{% endfor %}
15+
16+
# Docker bug makes rlwrap crash w/o short sleep first
17+
# Bug: https://github.com/moby/moby/issues/28009
18+
# As of 2021-09-10 this bug still exists, despite that issue being closed
19+
COPY rlwrap.retry /usr/local/bin/rlwrap

src/docker_clojure/config.clj

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,19 +105,20 @@
105105
:default :debian/bookworm})
106106

107107
(def build-tools
108-
{"lein" "2.12.0"
108+
{"lein" "2.13.0"
109109
"tools-deps" "1.12.5.1654"})
110110

111111
(def default-build-tool "tools-deps")
112112

113113
(def installer-hashes
114-
{"lein" {"2.11.2" "28a1a62668c5f427b413a8677e376affaa995f023b1fcd06e2d4c98ac1df5f3e"
115-
"2.12.0" "12a9c5e3a2471619ca3d64a7462f920fdf713ae8959eb4fcd6257c23332b5aa4"}
116-
"tools-deps" {"1.12.5.1645" "3d6e8428fd4c7f76de9f87f77b2347f293109f4e88fb20c154b3fa34a7f687dd"
114+
{"tools-deps" {"1.12.5.1645" "3d6e8428fd4c7f76de9f87f77b2347f293109f4e88fb20c154b3fa34a7f687dd"
117115
"1.12.5.1654" "28f81b0833c0a072f4370ae0eb1e4c5a4f9f4a34035cd7607ea9f253a8b06da1"}})
118116

119117
(def exclusions ; don't build these for whatever reason(s)
120-
#{;; No more jammy builds for JDK 23+
118+
#{;; Leiningen 2.13.0+ requires Java 11+
119+
{:jdk-version #(< % 11)
120+
:build-tool "lein"}
121+
;; No more jammy builds for JDK 23+
121122
{:jdk-version #(>= % 23)
122123
:distro :ubuntu/jammy}
123124
;; No upstream ARM alpine images available before JDK 21

src/docker_clojure/dockerfile.clj

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[clojure.string :as str]
55
[docker-clojure.dockerfile.lein :as lein]
66
[docker-clojure.dockerfile.tools-deps :as tools-deps]
7-
[docker-clojure.dockerfile.shared :refer [copy-resource-file! entrypoint]]
7+
[docker-clojure.dockerfile.shared :refer [copy-resource-file! render-template]]
88
[docker-clojure.log :refer [log]]))
99

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

22-
(defn all-contents [installer-hashes variant]
23-
(concat
24-
["" "### INSTALL LEIN ###"]
25-
(lein/install
26-
installer-hashes
27-
(assoc variant :build-tool-version
28-
(get-in variant [:build-tool-versions "lein"])))
29-
["" "### INSTALL TOOLS-DEPS ###"]
30-
(tools-deps/install
31-
installer-hashes
32-
(assoc variant :build-tool-version
33-
(get-in variant [:build-tool-versions "tools-deps"])))
34-
[""]
35-
(entrypoint variant)
36-
["" "CMD [\"-M\", \"--repl\"]"]))
22+
(defn copy-java?
23+
"Debian variants copy the JDK in from an eclipse-temurin image; the temurin
24+
base images already have it."
25+
[{:keys [distro]}]
26+
(contains? #{:debian :debian-slim} (-> distro namespace keyword)))
3727

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

45-
(defn contents [installer-hashes {:keys [build-tool distro] :as variant}]
46-
(str/join "\n"
47-
(concat [(format "FROM %s" (:base-image-tag variant))
48-
""]
49-
(case (-> distro namespace keyword)
50-
(:debian :debian-slim) (copy-java-from-temurin-contents variant)
51-
[])
52-
(case build-tool
53-
:docker-clojure.core/all (all-contents installer-hashes variant)
54-
"lein" (lein/contents installer-hashes variant)
55-
"tools-deps" (tools-deps/contents installer-hashes variant)))))
33+
(defn for-build-tool
34+
"Return `variant` with `:build-tool-version` set to the given tool's version.
35+
Single-build-tool variants already carry it; the combined `latest` image
36+
pulls each tool's version from `:build-tool-versions`."
37+
[variant build-tool]
38+
(cond-> variant
39+
(nil? (:build-tool-version variant))
40+
(assoc :build-tool-version (get-in variant [:build-tool-versions build-tool]))))
41+
42+
(defn body
43+
"The build-tool install section(s) that go between the base image setup and
44+
the entrypoint. The combined `latest` image stacks both behind headers."
45+
[installer-hashes {:keys [build-tool] :as variant}]
46+
(case build-tool
47+
"lein" (lein/install installer-hashes variant)
48+
"tools-deps" (tools-deps/install installer-hashes variant)
49+
:docker-clojure.core/all
50+
(str "\n### INSTALL LEIN ###\n"
51+
(lein/install installer-hashes (for-build-tool variant "lein"))
52+
"\n\n### INSTALL TOOLS-DEPS ###\n"
53+
(tools-deps/install installer-hashes (for-build-tool variant "tools-deps")))))
54+
55+
(defn command
56+
[{:keys [build-tool] :as variant}]
57+
(case build-tool
58+
"lein" (lein/command variant)
59+
"tools-deps" (tools-deps/command variant)
60+
:docker-clojure.core/all "CMD [\"-M\", \"--repl\"]"))
61+
62+
(defn contents [installer-hashes variant]
63+
(render-template
64+
"templates/Dockerfile.tmpl"
65+
{:from (:base-image-tag variant)
66+
:copy-java (copy-java? variant)
67+
:jdk-version (:jdk-version variant)
68+
:body (body installer-hashes variant)
69+
:entrypoint (entrypoint? variant)
70+
:cmd (command variant)}))
5671

5772
(defn shared-prereqs [dir {:keys [build-tool]}]
5873
(let [entrypoint (case build-tool
Lines changed: 25 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,45 @@
11
(ns docker-clojure.dockerfile.lein
2-
(:require [clojure.string :as str]
3-
[docker-clojure.dockerfile.shared
4-
:refer [concat-commands entrypoint install-distro-deps
2+
(:require [docker-clojure.dockerfile.shared
3+
:refer [install-distro-deps render-template
54
uninstall-distro-build-deps]]))
65

76
(defn prereqs [_ _] nil)
87

8+
;; Leiningen no longer publishes a standalone uberjar, so we build it from
9+
;; source: clone the GPG-signed release tag, bootstrap leiningen-core's deps
10+
;; with Maven, and run `lein uberjar`. That needs git, gnupg & Maven at build
11+
;; time.
912
(def distro-deps
10-
{:debian-slim {:build #{"wget" "gnupg"}
13+
{:debian-slim {:build #{"git" "gnupg" "maven"}
1114
:runtime #{}}
12-
:debian {:build #{"wget" "gnupg"}
15+
:debian {:build #{"git" "gnupg" "maven"}
1316
:runtime #{"make"}}
14-
:ubuntu {:build #{"wget" "gnupg"}
17+
:ubuntu {:build #{"git" "gnupg" "maven"}
1518
:runtime #{"make"}}
16-
:alpine {:build #{"tar" "gnupg" "openssl" "ca-certificates"}
19+
:alpine {:build #{"git" "gnupg" "maven" "ca-certificates"}
1720
:runtime #{"bash"}}})
1821

1922
(def install-deps (partial install-distro-deps distro-deps))
2023

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

23-
(def ^:const old-key "6A2D483DB59437EBB97D09B1040193357D0606ED")
24-
(def ^:const new-key "9D13D9426A0814B3373CF5E3D8A8243577A7859F")
26+
;; Clojure version pre-installed into lein images so users don't download it on
27+
;; first use.
28+
(def ^:const bundled-clojure-version "1.12.5")
2529

26-
(defn gpg-key
27-
[version]
28-
(let [[major minor] (map #(Integer/parseInt %) (str/split version #"\."))]
29-
(cond
30-
(< 2 major) new-key
31-
(and (= 2 major) (< 10 minor)) new-key
32-
:else old-key)))
30+
;; Leiningen release tags are signed with this key (Phil Hagelberg).
31+
(def ^:const signing-key "9D13D9426A0814B3373CF5E3D8A8243577A7859F")
3332

34-
(defn install [installer-hashes {:keys [build-tool-version] :as variant}]
35-
(let [install-dep-cmds (install-deps variant)
36-
uninstall-dep-cmds (uninstall-build-deps variant)]
37-
(-> [(format "ENV LEIN_VERSION=%s" build-tool-version)
38-
"ENV LEIN_INSTALL=/usr/local/bin/"
39-
""
40-
"WORKDIR /tmp"
41-
""
42-
"# Download the whole repo as an archive"
43-
"RUN set -eux; \\"]
44-
(concat-commands install-dep-cmds)
45-
(concat-commands
46-
["mkdir -p $LEIN_INSTALL"
47-
"wget -q https://codeberg.org/leiningen/leiningen/raw/tag/$LEIN_VERSION/bin/lein-pkg"
48-
"echo \"Comparing lein-pkg checksum ...\""
49-
"sha256sum lein-pkg"
50-
(str "echo \"" (get-in installer-hashes ["lein" build-tool-version]) " *lein-pkg\" | sha256sum -c -")
51-
"mv lein-pkg $LEIN_INSTALL/lein"
52-
"chmod 0755 $LEIN_INSTALL/lein"
53-
"export GNUPGHOME=\"$(mktemp -d)\""
54-
"export FILENAME_EXT=jar" ; used to be zip but hopefully it's always jar now?
55-
(str "gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys "
56-
(gpg-key build-tool-version))
57-
"wget -q https://codeberg.org/leiningen/leiningen/releases/download/$LEIN_VERSION/leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT"
58-
"wget -q https://codeberg.org/leiningen/leiningen/releases/download/$LEIN_VERSION/leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT.asc"
59-
"echo \"Verifying file PGP signature...\""
60-
"gpg --batch --verify leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT.asc leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT"
61-
"gpgconf --kill all"
62-
"rm -rf \"$GNUPGHOME\" leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT.asc"
63-
"mkdir -p /usr/share/java"
64-
"mkdir -p /root/.lein"
65-
"mv leiningen-$LEIN_VERSION-standalone.$FILENAME_EXT /usr/share/java/leiningen-$LEIN_VERSION-standalone.jar"]
66-
(empty? uninstall-dep-cmds))
67-
(concat-commands uninstall-dep-cmds :end)
68-
(concat
69-
[""
70-
"ENV PATH=$PATH:$LEIN_INSTALL"
71-
"ENV LEIN_ROOT 1"
72-
""
73-
"# Install clojure 1.12.1 so users don't have to download it every time"
74-
"RUN echo '(defproject dummy \"\" :dependencies [[org.clojure/clojure \"1.12.1\"]])' > project.clj \\"
75-
" && lein deps && rm project.clj"])
76-
77-
(->> (remove nil?)))))
33+
(defn install [_installer-hashes {:keys [build-tool-version] :as variant}]
34+
(render-template
35+
"templates/lein.tmpl"
36+
{:lein-version build-tool-version
37+
:clojure-version bundled-clojure-version
38+
:gpg-key signing-key
39+
:install-deps (install-deps variant)
40+
:uninstall-deps (uninstall-build-deps variant)}))
7841

7942
(defn command [{:keys [jdk-version]}]
8043
(if (>= jdk-version 16)
81-
["CMD [\"repl\"]"]
82-
["CMD [\"lein\", \"repl\"]"]))
83-
84-
(defn contents [installer-hashes variant]
85-
(concat (install installer-hashes variant) [""] (entrypoint variant) (command variant)))
44+
"CMD [\"repl\"]"
45+
"CMD [\"lein\", \"repl\"]"))

src/docker_clojure/dockerfile/shared.clj

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
(ns docker-clojure.dockerfile.shared
2-
(:require [clojure.string :as str]
3-
[clojure.java.io :as io]))
2+
(:require [clojure.java.io :as io]
3+
[clojure.string :as str]
4+
[selmer.parser :as selmer]
5+
[selmer.util :as selmer-util]))
46

5-
(defn concat-commands [base cmds & [end?]]
6-
(let [commands (if end?
7-
(butlast cmds)
8-
cmds)]
9-
(concat base
10-
(map #(str % " && \\")
11-
commands)
12-
(when end? [(last cmds)]))))
7+
;; Dockerfiles aren't HTML, so don't let Selmer escape any of the values we
8+
;; interpolate (quotes, brackets, etc. must pass through verbatim).
9+
(selmer-util/turn-off-escaping!)
10+
11+
(defn render-template
12+
"Render the Selmer template at resource path `tmpl` with `context`, trimming
13+
any trailing newline so callers control the surrounding whitespace."
14+
[tmpl context]
15+
(-> tmpl io/resource slurp (selmer/render context) str/trim-newline))
1316

1417
(defn get-deps [type distro-deps distro]
1518
(some->> distro namespace keyword (get distro-deps) type))
@@ -61,14 +64,3 @@
6164
dest (io/file build-dir filename)]
6265
(->> src slurp contents-processor (spit dest))
6366
(file-processor dest))))
64-
65-
(defn entrypoint
66-
"This is the same for every build-tool so far, so it's in here. If that
67-
changes move it into the build-tool-specific namespaces (or future protocol)."
68-
[{:keys [jdk-version]}]
69-
(if (>= jdk-version 16)
70-
(concat
71-
["COPY entrypoint /usr/local/bin/entrypoint"]
72-
[""]
73-
["ENTRYPOINT [\"entrypoint\"]"])
74-
nil))

0 commit comments

Comments
 (0)