|
1 | 1 | (ns docker-clojure.core |
2 | 2 | (:require |
| 3 | + [clojure.core.async :refer [<!! chan pipeline-blocking to-chan!] :as async] |
| 4 | + [clojure.edn :as edn] |
3 | 5 | [clojure.java.io :as io] |
4 | | - [clojure.java.shell :refer [sh with-sh-dir]] |
5 | | - [clojure.math.combinatorics :as combo] |
| 6 | + [clojure.java.shell :refer [sh]] |
6 | 7 | [clojure.spec.alpha :as s] |
7 | 8 | [clojure.string :as str] |
8 | | - [clojure.core.async :refer [<!! chan to-chan! pipeline-blocking] :as async] |
9 | 9 | [docker-clojure.config :as cfg] |
| 10 | + [docker-clojure.docker :as docker] |
10 | 11 | [docker-clojure.dockerfile :as df] |
11 | | - [docker-clojure.manifest :as manifest] |
12 | | - [docker-clojure.util :refer [get-or-default default-docker-tag |
13 | | - full-docker-tag]] |
14 | 12 | [docker-clojure.log :refer [log] :as logger] |
15 | | - [clojure.edn :as edn])) |
16 | | - |
17 | | -(defn exclude-variant? |
18 | | - "Returns true if the map `variant` contains every key-value pair in the map |
19 | | - `exclusion`. `variant` may contain additional keys that are not in |
20 | | - `exclusion`. Some values of `exclusion` can also be a predicate of one |
21 | | - argument which is then tested against the respective value from `variant`. |
22 | | - Returns false if any of the keys in `exclusions` are missing from `variant` or |
23 | | - have different values, or the predicate value returned false." |
24 | | - [variant exclusion] |
25 | | - (every? (fn [[k v]] |
26 | | - (if (fn? v) |
27 | | - (v (get variant k)) |
28 | | - (= v (get variant k)))) |
29 | | - exclusion)) |
30 | | - |
31 | | -(defn base-image-tag |
32 | | - [base-image jdk-version distro] |
33 | | - (str base-image ":" |
34 | | - (case base-image |
35 | | - "eclipse-temurin" (str jdk-version "-jdk-") |
36 | | - "debian" "" |
37 | | - "-") |
38 | | - (name distro))) |
| 13 | + [docker-clojure.manifest :as manifest] |
| 14 | + [docker-clojure.util :refer [get-or-default]] |
| 15 | + [docker-clojure.variant :as variant])) |
39 | 16 |
|
40 | 17 | (defn exclude? |
41 | 18 | "Returns true if `variant` matches one of `exclusions` elements (meaning |
42 | 19 | `(contains-every-key-value? variant exclusion)` returns true)." |
43 | 20 | [exclusions variant] |
44 | | - (some (partial exclude-variant? variant) exclusions)) |
| 21 | + (some (partial variant/exclude? variant) exclusions)) |
45 | 22 |
|
46 | 23 | (s/def ::variant |
47 | 24 | (s/keys :req-un [::cfg/jdk-version ::cfg/base-image ::cfg/base-image-tag |
48 | 25 | ::cfg/distro ::cfg/build-tool ::cfg/build-tool-version |
49 | 26 | ::cfg/maintainer ::cfg/docker-tag] |
50 | | - :opt-un [::cfg/build-tool-versions ::cfg/architectures])) |
51 | | - |
52 | | -(defn assoc-if |
53 | | - [m pred k v] |
54 | | - (if (pred) |
55 | | - (assoc m k v) |
56 | | - m)) |
57 | | - |
58 | | -(defn variant-map [[base-image jdk-version distro |
59 | | - [build-tool build-tool-version]]] |
60 | | - (let [variant-arch (get cfg/distro-architectures |
61 | | - (-> distro namespace keyword)) |
62 | | - base {:jdk-version jdk-version |
63 | | - :base-image base-image |
64 | | - :base-image-tag (base-image-tag base-image |
65 | | - jdk-version distro) |
66 | | - :distro distro |
67 | | - :build-tool build-tool |
68 | | - :build-tool-version build-tool-version |
69 | | - :maintainer (str/join " & " cfg/maintainers)}] |
70 | | - (-> base |
71 | | - (assoc :docker-tag (default-docker-tag base)) |
72 | | - (assoc-if #(nil? (:build-tool-version base)) :build-tool-versions |
73 | | - cfg/build-tools) |
74 | | - (assoc-if #(seq variant-arch) :architectures variant-arch)))) |
75 | | - |
76 | | -(defn pull-image [image] |
77 | | - (sh "docker" "pull" image)) |
| 27 | + :opt-un [::cfg/build-tool-versions ::cfg/architecture])) |
78 | 28 |
|
79 | | -(defn generate-dockerfile! [installer-hashes variant] |
80 | | - (let [build-dir (df/build-dir variant) |
81 | | - filename "Dockerfile"] |
82 | | - (log "Generating" (str build-dir "/" filename)) |
83 | | - (df/write-file build-dir filename installer-hashes variant) |
84 | | - (assoc variant |
85 | | - :build-dir build-dir |
86 | | - :dockerfile filename))) |
87 | | - |
88 | | -(defn build-image |
89 | | - [installer-hashes {:keys [docker-tag base-image architectures] :as variant}] |
90 | | - (let [image-tag (str "clojure:" docker-tag) |
91 | | - _ (log "Pulling base image" base-image) |
92 | | - _ (pull-image base-image) |
93 | | - |
94 | | - {:keys [dockerfile build-dir]} |
95 | | - (generate-dockerfile! installer-hashes variant) |
96 | | - |
97 | | - host-arch (let [jvm-arch (System/getProperty "os.arch")] |
98 | | - (if (= "aarch64" jvm-arch) |
99 | | - "arm64v8" |
100 | | - jvm-arch)) |
101 | | - platform-flag (if (contains? (or architectures |
102 | | - cfg/default-architectures) |
103 | | - host-arch) |
104 | | - nil |
105 | | - (str "--platform=linux/" (first architectures))) |
106 | | - |
107 | | - build-cmd (remove nil? ["docker" "buildx" "build" "--no-cache" |
108 | | - "-t" image-tag platform-flag "--load" |
109 | | - "-f" dockerfile "."])] |
110 | | - (apply log "Running" build-cmd) |
111 | | - (let [{:keys [out err exit]} |
112 | | - (with-sh-dir build-dir (apply sh build-cmd))] |
113 | | - (if (zero? exit) |
114 | | - (log "Succeeded building" (str "clojure:" docker-tag)) |
115 | | - (log "ERROR building" (str "clojure:" docker-tag ":") err out)))) |
116 | | - (log) |
117 | | - [::done variant]) |
118 | | - |
119 | | -(def latest-variant |
| 29 | +(def latest-variants |
120 | 30 | "The latest variant is special because we include all 3 build tools via the |
121 | 31 | [::all] value on the end." |
122 | | - (list (-> cfg/base-images :default first) |
123 | | - cfg/default-jdk-version |
124 | | - (get-or-default cfg/default-distros cfg/default-jdk-version) |
125 | | - [::all])) |
126 | | - |
127 | | -(defn image-variant-combinations |
128 | | - [base-images jdk-versions distros build-tools] |
129 | | - (reduce |
130 | | - (fn [variants jdk-version] |
131 | | - (concat |
132 | | - variants |
133 | | - (let [jdk-base-images (get-or-default base-images jdk-version)] |
134 | | - (loop [[bi & r] jdk-base-images |
135 | | - acc #{}] |
136 | | - (let [vs (combo/cartesian-product #{bi} |
137 | | - #{jdk-version} |
138 | | - (get-or-default distros bi) |
139 | | - build-tools) |
140 | | - acc' (concat acc vs)] |
141 | | - (if (seq r) |
142 | | - (recur r acc') |
143 | | - acc')))))) |
144 | | - #{} jdk-versions)) |
| 32 | + (for [arch cfg/architectures] |
| 33 | + (list (-> cfg/base-images :default first) |
| 34 | + cfg/default-jdk-version |
| 35 | + (get-or-default cfg/default-distros cfg/default-jdk-version) |
| 36 | + [::all] |
| 37 | + arch))) |
145 | 38 |
|
146 | 39 | (defn image-variants |
147 | | - [base-images jdk-versions distros build-tools] |
| 40 | + [base-images jdk-versions distros build-tools architectures] |
148 | 41 | (into #{} |
149 | 42 | (comp |
150 | | - (map variant-map) |
| 43 | + (map variant/->map) |
151 | 44 | (remove #(= ::s/invalid (s/conform ::variant %)))) |
152 | | - (conj |
153 | | - (image-variant-combinations base-images jdk-versions distros |
154 | | - build-tools) |
155 | | - latest-variant))) |
| 45 | + (concat |
| 46 | + (variant/combinations base-images jdk-versions distros build-tools |
| 47 | + architectures) |
| 48 | + latest-variants))) |
156 | 49 |
|
157 | 50 | (defn rand-delay |
158 | 51 | "Runs argument f w/ any supplied args after a random delay of 100-1000 ms" |
|
164 | 57 | (defn build-images |
165 | 58 | [parallelization installer-hashes variants] |
166 | 59 | (log "Building images" parallelization "at a time") |
167 | | - (let [variants-ch (to-chan! variants) |
168 | | - builds-ch (chan parallelization)] |
| 60 | + (let [variants-ch (to-chan! variants) |
| 61 | + builds-ch (chan parallelization)] |
169 | 62 | ;; Kick off builds with a random delay so we don't have Docker race |
170 | 63 | ;; conditions (e.g. build container name collisions) |
171 | 64 | (async/thread (pipeline-blocking parallelization builds-ch |
172 | | - (map (partial rand-delay build-image |
| 65 | + (map (partial rand-delay docker/build-image |
173 | 66 | installer-hashes)) |
174 | 67 | variants-ch)) |
175 | 68 | (while (<!! builds-ch)))) |
176 | 69 |
|
177 | 70 | (defn generate-dockerfiles! [installer-hashes variants] |
178 | 71 | (log "Generated" (count variants) "variants") |
179 | 72 | (doseq [variant variants] |
180 | | - (generate-dockerfile! installer-hashes variant))) |
| 73 | + (df/generate! installer-hashes variant))) |
181 | 74 |
|
182 | 75 | (defn valid-variants [] |
183 | 76 | (remove (partial exclude? cfg/exclusions) |
184 | 77 | (image-variants cfg/base-images cfg/jdk-versions cfg/distros |
185 | | - cfg/build-tools))) |
| 78 | + cfg/build-tools cfg/architectures))) |
186 | 79 |
|
187 | 80 | (defn generate-manifest! [variants args] |
188 | 81 | (let [git-head (->> ["git" "rev-parse" "HEAD"] (apply sh) :out) |
189 | 82 | target-file (or (first args) :stdout) |
190 | 83 | manifest (manifest/generate {:maintainers cfg/maintainers |
191 | | - :architectures cfg/default-architectures |
| 84 | + :architectures cfg/architectures |
192 | 85 | :git-repo cfg/git-repo} |
193 | 86 | git-head variants)] |
194 | 87 | (log "Writing manifest of" (count variants) "variants to" target-file "...") |
|
199 | 92 | (when (not= :stdout target-file) |
200 | 93 | (.close output-writer))))) |
201 | 94 |
|
202 | | -(defn sort-variants |
203 | | - [variants] |
204 | | - (sort |
205 | | - (fn [v1 v2] |
206 | | - (cond |
207 | | - (= "latest" (:docker-tag v1)) -1 |
208 | | - (= "latest" (:docker-tag v2)) 1 |
209 | | - :else (let [c (compare (:jdk-version v1) (:jdk-version v2))] |
210 | | - (if (not= c 0) |
211 | | - c |
212 | | - (let [c (compare (full-docker-tag v1) (full-docker-tag v2))] |
213 | | - (if (not= c 0) |
214 | | - c |
215 | | - (throw |
216 | | - (ex-info "No two variants should have the same full Docker tag" |
217 | | - {:v1 v1, :v2 v2})))))))) |
218 | | - variants)) |
219 | | - |
220 | 95 | (defn generate-variants |
221 | 96 | [args] |
222 | 97 | ; TODO: Maybe replace this with bb/cli |
|
247 | 122 | (case cmd |
248 | 123 | :clean (df/clean-all) |
249 | 124 | :dockerfiles (generate-dockerfiles! cfg/installer-hashes variants) |
250 | | - :manifest (-> variants sort-variants (generate-manifest! args)) |
| 125 | + :manifest (generate-manifest! variants args) |
251 | 126 | :build-images (build-images parallelization cfg/installer-hashes variants))) |
252 | 127 | (logger/stop)) |
253 | 128 |
|
|
0 commit comments