diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb48e00f..faa49a6a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: - name: Install babashka uses: DeLaGuardo/setup-clojure@12.5 with: - bb: 1.3.190 + bb: 1.12.199 - uses: actions/checkout@v4 - name: Check for stale Dockerfiles run: | diff --git a/.github/workflows/official-images-pr.yml b/.github/workflows/official-images-pr.yml index c5a6d4c8..dddf60ea 100644 --- a/.github/workflows/official-images-pr.yml +++ b/.github/workflows/official-images-pr.yml @@ -12,9 +12,9 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Install babashka - uses: DeLaGuardo/setup-clojure@12.5 + uses: DeLaGuardo/setup-clojure@13.2 with: - bb: 1.3.190 + bb: 1.12.199 - name: Checkout code uses: actions/checkout@v4 - name: Generate manifest @@ -67,7 +67,7 @@ jobs: github-username: ${{ github.actor }} token: ${{ secrets.API_TOKEN_GITHUB }} - name: Open official-images pull request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v7 with: token: ${{ secrets.API_TOKEN_GITHUB }} path: official-images diff --git a/bb.edn b/bb.edn index d1828062..466b9f54 100644 --- a/bb.edn +++ b/bb.edn @@ -1,15 +1,13 @@ {:paths ["src"] :deps {local/deps {:local/root "."} org.babashka/spec.alpha {:git/url "https://github.com/babashka/spec.alpha" - :git/sha "951b49b8c173244e66443b8188e3ff928a0a71e7"}} + :git/sha "b6eb0f2208ab036c0a5d0e7235cb0b09d2feabb7"}} :tasks - {:requires ([docker-clojure.core :as dc]) + {:requires ([babashka.deps :as deps] + [docker-clojure.core :as dc]) clean (dc/-main "clean") dockerfiles {:depends [clean] :task (apply dc/-main "dockerfiles" *command-line-args*)} manifest (apply dc/-main "manifest" *command-line-args*) build-images {:task (apply dc/-main "build-images" *command-line-args*)} - test {:extra-paths ["test"] - :requires ([docker-clojure.test-runner :as tr]) - :task (tr/-main 'docker-clojure.core-test 'docker-clojure.dockerfile-test - 'docker-clojure.manifest-test)}}} + test (deref (deps/clojure '-X:test))}} diff --git a/deps.edn b/deps.edn index e127716b..2dda7f87 100644 --- a/deps.edn +++ b/deps.edn @@ -1,7 +1,9 @@ {:deps {org.clojure/clojure {:mvn/version "1.12.0"} org.clojure/math.combinatorics {:mvn/version "0.3.0"} - org.clojure/core.async {:mvn/version "1.6.681"}} + org.clojure/core.async {:mvn/version "1.8.741"} + com.gfredericks/test.chuck {:git/url "https://github.com/gfredericks/test.chuck" + :git/sha "3e129a11ce9cc1a57584fa022b9b05ab7546a609"}} :paths ["src" "resources"] @@ -11,7 +13,8 @@ :parallelization 2}} :test {:extra-paths ["test"] - :extra-deps {com.cognitect/test-runner - {:git/url "https://github.com/cognitect-labs/test-runner.git" - :sha "9d36f36ff541dac680a05010e4348c744333f191"}} - :main-opts ["-m" "cognitect.test-runner"]}}} + :extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"} + orchestra/orchestra {:mvn/version "2021.01.01-1"} + org.clojure/test.check {:mvn/version "1.1.1"}} + :exec-fn docker-clojure.fix-kaocha/run-tests + :exec-args {}}}} diff --git a/src/docker_clojure/config.clj b/src/docker_clojure/config.clj index 379cc119..a61284d6 100644 --- a/src/docker_clojure/config.clj +++ b/src/docker_clojure/config.clj @@ -1,6 +1,8 @@ (ns docker-clojure.config (:require [clojure.spec.alpha :as s] + [clojure.spec.gen.alpha :as gen] [clojure.string :as str] + [com.gfredericks.test.chuck.generators :as gen'] [docker-clojure.core :as-alias core])) (s/def ::non-blank-string @@ -13,25 +15,60 @@ (s/def ::base-image ::non-blank-string) (s/def ::base-images (s/coll-of ::base-image :distinct true :into #{})) -(s/def ::docker-image-name (s/and ::non-blank-string - #(re-matches #"[-\w]+(?::[-\w.]+)?" %))) -(s/def ::docker-tag (s/and ::non-blank-string - #(re-matches #"[-\w.]+" %))) +(def docker-image-name-re (re-pattern "[-\\w]+(?::[-\\w.]+)?")) + +(s/def ::docker-image-name + (s/with-gen + (s/and ::non-blank-string + #(re-matches docker-image-name-re %)) + #(gen'/string-from-regex docker-image-name-re))) + +(def docker-tag-re (re-pattern "[-\\w.]+")) + +(s/def ::docker-tag + (s/with-gen + (s/and ::non-blank-string + #(re-matches docker-tag-re %)) + #(gen'/string-from-regex docker-tag-re))) + (s/def ::base-image-tag ::docker-image-name) -(s/def ::distro qualified-keyword?) +(def distro-component-re (re-pattern "[-_A-Za-z][-\\w.]+")) + +(s/def ::distro + (s/with-gen + (s/and qualified-keyword? + #(->> % + ((juxt namespace name)) + ((fn [elements] + (every? (fn [e] (re-matches distro-component-re e)) + elements))))) + #(gen/fmap (fn [[namespace local]] (keyword namespace local)) + (gen/vector (gen'/string-from-regex distro-component-re) 2)))) + (s/def ::distros (s/coll-of ::distro :distinct true :into #{})) -(s/def ::build-tool (s/or ::specific-tool ::non-blank-string - ::all-tools #(= ::core/all %))) +(s/def ::specific-build-tool #{"lein" "tools-deps"}) +(s/def ::build-tool (s/or ::specific-tool ::specific-build-tool + ::all-tools #{::core/all})) +(s/def ::specific-build-tool-version + (s/with-gen + (s/and ::non-blank-string + #(re-matches #"(?:\d+\.)+\d+" %)) + #(gen/fmap (fn [nums] (str/join "." nums)) + (gen/vector (gen/int) 2 4)))) + (s/def ::build-tool-version - (s/nilable (s/and ::non-blank-string #(re-matches #"[\d\.]+" %)))) -(s/def ::build-tools (s/map-of ::build-tool ::build-tool-version)) + (s/nilable ::specific-build-tool-version)) + +(s/def ::build-tool-versions + (s/map-of ::specific-build-tool ::specific-build-tool-version)) (s/def ::maintainers (s/coll-of ::non-blank-string :distinct true :into #{})) +(s/def ::maintainer ::non-blank-string) -(s/def ::architecture ::non-blank-string) +(s/def ::architecture #{"amd64" "arm64v8"}) (s/def ::architectures (s/coll-of ::architecture :distinct true :into #{})) (def git-repo "https://github.com/Quantisan/docker-clojure.git") @@ -55,14 +92,9 @@ "debian" #{:debian-slim/bookworm-slim :debian/bookworm :debian-slim/bullseye-slim :debian/bullseye}}) -(def default-architectures +(def architectures #{"amd64" "arm64v8"}) -(def distro-architectures - "Map of distro types to architectures it supports if different from - default-architectures." - {:alpine #{"amd64"}}) - (def default-distros "The default distro to use for tags that don't specify one, keyed by jdk-version. :default is a fallback for jdk versions not o/w specified." @@ -84,12 +116,13 @@ "1.12.0.1530" "2a113e3a4f1005e05f4d6a6dee24ca317b0115cdd7e6ca6155a76f5ffa5ba35b"}}) (def exclusions ; don't build these for whatever reason(s) - #{; no more jammy builds for JDK 23+ + #{;; No more jammy builds for JDK 23+ {:jdk-version #(>= % 23) :distro :ubuntu/jammy} - ;; commented out example - #_{:jdk-version 8 - :distro :alpine/alpine}}) + ;; No upstream ARM alpine images available before JDK 21 + {:jdk-version #(< % 21) + :architecture "arm64v8" + :distro :alpine/alpine}}) (def maintainers ["Paul Lam (@Quantisan)" diff --git a/src/docker_clojure/core.clj b/src/docker_clojure/core.clj index 0c364b89..4e734f1f 100644 --- a/src/docker_clojure/core.clj +++ b/src/docker_clojure/core.clj @@ -1,158 +1,53 @@ (ns docker-clojure.core (:require + [clojure.core.async :refer [ distro namespace keyword)) - base {:jdk-version jdk-version - :base-image base-image - :base-image-tag (base-image-tag base-image - jdk-version distro) - :distro distro - :build-tool build-tool - :build-tool-version build-tool-version - :maintainer (str/join " & " cfg/maintainers)}] - (-> base - (assoc :docker-tag (default-docker-tag base)) - (assoc-if #(nil? (:build-tool-version base)) :build-tool-versions - cfg/build-tools) - (assoc-if #(seq variant-arch) :architectures variant-arch)))) - -(defn pull-image [image] - (sh "docker" "pull" image)) + (some (partial variant/exclude? variant) exclusions)) -(defn generate-dockerfile! [installer-hashes variant] - (let [build-dir (df/build-dir variant) - filename "Dockerfile"] - (log "Generating" (str build-dir "/" filename)) - (df/write-file build-dir filename installer-hashes variant) - (assoc variant - :build-dir build-dir - :dockerfile filename))) - -(defn build-image - [installer-hashes {:keys [docker-tag base-image architectures] :as variant}] - (let [image-tag (str "clojure:" docker-tag) - _ (log "Pulling base image" base-image) - _ (pull-image base-image) - - {:keys [dockerfile build-dir]} - (generate-dockerfile! installer-hashes variant) - - host-arch (let [jvm-arch (System/getProperty "os.arch")] - (if (= "aarch64" jvm-arch) - "arm64v8" - jvm-arch)) - platform-flag (if (contains? (or architectures - cfg/default-architectures) - host-arch) - nil - (str "--platform=linux/" (first architectures))) - - build-cmd (remove nil? ["docker" "buildx" "build" "--no-cache" - "-t" image-tag platform-flag "--load" - "-f" dockerfile "."])] - (apply log "Running" build-cmd) - (let [{:keys [out err exit]} - (with-sh-dir build-dir (apply sh build-cmd))] - (if (zero? exit) - (log "Succeeded building" (str "clojure:" docker-tag)) - (log "ERROR building" (str "clojure:" docker-tag ":") err out)))) - (log) - [::done variant]) - -(def latest-variant +(def latest-variants "The latest variant is special because we include all 3 build tools via the [::all] value on the end." - (list (-> cfg/base-images :default first) - cfg/default-jdk-version - (get-or-default cfg/default-distros cfg/default-jdk-version) - [::all])) - -(defn image-variant-combinations - [base-images jdk-versions distros build-tools] - (reduce - (fn [variants jdk-version] - (concat - variants - (let [jdk-base-images (get-or-default base-images jdk-version)] - (loop [[bi & r] jdk-base-images - acc #{}] - (let [vs (combo/cartesian-product #{bi} - #{jdk-version} - (get-or-default distros bi) - build-tools) - acc' (concat acc vs)] - (if (seq r) - (recur r acc') - acc')))))) - #{} jdk-versions)) + (for [arch cfg/architectures] + (list (-> cfg/base-images :default first) + cfg/default-jdk-version + (get-or-default cfg/default-distros cfg/default-jdk-version) + [::all] + arch))) + +(defn- invalid-variant? + [variant] + (let [status (s/conform ::variant/variant variant) + invalid? (= ::s/invalid status)] + (when invalid? + (println "invalid variant:" (pr-str variant)) + true))) (defn image-variants - [base-images jdk-versions distros build-tools] + [base-images jdk-versions distros build-tools architectures] (into #{} (comp - (map variant-map) - (remove #(= ::s/invalid (s/conform ::variant %)))) - (conj - (image-variant-combinations base-images jdk-versions distros - build-tools) - latest-variant))) + (map variant/->map) + (remove invalid-variant?)) + (concat + (variant/combinations base-images jdk-versions distros build-tools + architectures) + latest-variants))) (defn rand-delay "Runs argument f w/ any supplied args after a random delay of 100-1000 ms" @@ -164,12 +59,12 @@ (defn build-images [parallelization installer-hashes variants] (log "Building images" parallelization "at a time") - (let [variants-ch (to-chan! variants) - builds-ch (chan parallelization)] + (let [variants-ch (to-chan! variants) + builds-ch (chan parallelization)] ;; Kick off builds with a random delay so we don't have Docker race ;; conditions (e.g. build container name collisions) (async/thread (pipeline-blocking parallelization builds-ch - (map (partial rand-delay build-image + (map (partial rand-delay docker/build-image installer-hashes)) variants-ch)) (while (> ["git" "rev-parse" "HEAD"] (apply sh) :out) target-file (or (first args) :stdout) manifest (manifest/generate {:maintainers cfg/maintainers - :architectures cfg/default-architectures + :architectures cfg/architectures :git-repo cfg/git-repo} git-head variants)] (log "Writing manifest of" (count variants) "variants to" target-file "...") @@ -199,24 +94,6 @@ (when (not= :stdout target-file) (.close output-writer))))) -(defn sort-variants - [variants] - (sort - (fn [v1 v2] - (cond - (= "latest" (:docker-tag v1)) -1 - (= "latest" (:docker-tag v2)) 1 - :else (let [c (compare (:jdk-version v1) (:jdk-version v2))] - (if (not= c 0) - c - (let [c (compare (full-docker-tag v1) (full-docker-tag v2))] - (if (not= c 0) - c - (throw - (ex-info "No two variants should have the same full Docker tag" - {:v1 v1, :v2 v2})))))))) - variants)) - (defn generate-variants [args] ; TODO: Maybe replace this with bb/cli @@ -247,7 +124,7 @@ (case cmd :clean (df/clean-all) :dockerfiles (generate-dockerfiles! cfg/installer-hashes variants) - :manifest (-> variants sort-variants (generate-manifest! args)) + :manifest (generate-manifest! variants args) :build-images (build-images parallelization cfg/installer-hashes variants))) (logger/stop)) diff --git a/src/docker_clojure/docker.clj b/src/docker_clojure/docker.clj new file mode 100644 index 00000000..c1013398 --- /dev/null +++ b/src/docker_clojure/docker.clj @@ -0,0 +1,115 @@ +(ns docker-clojure.docker + (:require [clojure.java.shell :refer [sh with-sh-dir]] + [clojure.string :as str] + [docker-clojure.config :as cfg] + [docker-clojure.core :as-alias core] + [docker-clojure.dockerfile :as df] + [docker-clojure.util :refer [get-or-default]] + [docker-clojure.log :refer [log]])) + +(defn pull-image [image] + (sh "docker" "pull" image)) + +(defn build-image + [installer-hashes {:keys [docker-tag base-image architecture] :as variant}] + (let [image-tag (str "clojure:" docker-tag) + _ (log "Pulling base image" base-image) + _ (pull-image base-image) + + {:keys [dockerfile build-dir]} + (df/generate! installer-hashes variant) + + host-arch (let [jvm-arch (System/getProperty "os.arch")] + (if (= "aarch64" jvm-arch) + "arm64v8" + jvm-arch)) + platform-flag (if (= architecture host-arch) + nil + (str "--platform=linux/" architecture)) + + build-cmd (remove nil? ["docker" "buildx" "build" "--no-cache" + "-t" image-tag platform-flag "--load" + "-f" dockerfile "."])] + (apply log "Running" build-cmd) + (let [{:keys [out err exit]} + (with-sh-dir build-dir (apply sh build-cmd))] + (if (zero? exit) + (log "Succeeded building" (str "clojure:" docker-tag)) + (log "ERROR building" (str "clojure:" docker-tag ":") err out)))) + (log) + [::done variant]) + +(defn base-image-tag + [base-image jdk-version distro] + (str base-image ":" + (case base-image + "eclipse-temurin" (str jdk-version "-jdk-") + "debian" "" + "-") + (name distro))) + +(defn jdk-label + [omit-default? jdk-version base-image] + (if (and omit-default? (= cfg/default-jdk-version jdk-version) + (= (first (get-or-default cfg/base-images jdk-version)) + base-image)) + nil + (str + (case base-image + ("eclipse-temurin" "debian") "temurin" + base-image) + "-" jdk-version))) + +(defn tag + "Returns the Docker tag for the given variant with truthy keys from first arg + left out when possible." + [{:keys [omit-all? omit-jdk? omit-build-tool? omit-build-tool-version? + omit-distro?]} + {:keys [base-image jdk-version distro build-tool + build-tool-version] :as _variant}] + (if (= ::core/all build-tool) + "latest" + (let [jdk (jdk-label (or omit-all? omit-jdk?) + jdk-version base-image) + dd (get-or-default cfg/default-distros jdk-version) + distro-label (if (and (or omit-all? omit-distro?) (= dd distro)) + nil + (when distro (name distro))) + tag-elements (remove nil? [jdk distro-label]) + build-tool-label (if (and (seq tag-elements) ; ensure tag is non-empty + (or omit-all? omit-build-tool?) + (= build-tool cfg/default-build-tool)) + nil + build-tool) + build-tool-version-label (if (or omit-all? omit-build-tool? + omit-build-tool-version?) + nil + build-tool-version)] + (str/join "-" (remove nil? [jdk build-tool-label + build-tool-version-label + distro-label]))))) + +(def full-tag + (partial tag {})) + +(def default-tag + (partial tag {:omit-jdk? true, :omit-distro? true})) + +(defn all-tags + "Returns all Docker tags for the given variant" + [variant] + (let [short-tag (:docker-tag variant) + full-tag (full-tag variant) + base (into #{} [short-tag full-tag])] + (-> base + (conj + (tag {:omit-jdk? true} variant) + (tag {:omit-build-tool? true} variant) + (tag {:omit-build-tool-version? true} variant) + (tag {:omit-distro? true} variant) + (tag {:omit-distro? true, :omit-build-tool-version? true} variant) + (tag {:omit-jdk? true, :omit-build-tool-version? true} variant) + (tag {:omit-jdk? true, :omit-distro? true + :omit-build-tool-version? true} variant)) + vec + sort))) diff --git a/src/docker_clojure/dockerfile.clj b/src/docker_clojure/dockerfile.clj index ab3321fe..e2aa056c 100644 --- a/src/docker_clojure/dockerfile.clj +++ b/src/docker_clojure/dockerfile.clj @@ -4,7 +4,8 @@ [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! entrypoint]] + [docker-clojure.log :refer [log]])) (defn build-dir [{:keys [base-image-tag jdk-version build-tool]}] (str/join "/" ["target" @@ -79,5 +80,14 @@ (throw (ex-info (str "Error creating directory " dir) {:error err}))))) +(defn generate! [installer-hashes variant] + (let [build-dir (build-dir variant) + filename "Dockerfile"] + (log "Generating" (str build-dir "/" filename)) + (write-file build-dir filename installer-hashes variant) + (assoc variant + :build-dir build-dir + :dockerfile filename))) + (defn clean-all [] (sh "sh" "-c" "rm -rf target/*")) diff --git a/src/docker_clojure/manifest.clj b/src/docker_clojure/manifest.clj index 7adb77f0..ef28044a 100644 --- a/src/docker_clojure/manifest.clj +++ b/src/docker_clojure/manifest.clj @@ -1,26 +1,8 @@ (ns docker-clojure.manifest (:require [clojure.string :as str] + [docker-clojure.docker :as docker] [docker-clojure.dockerfile :as df] - [docker-clojure.util :refer [docker-tag full-docker-tag]])) - -(defn variant-tags - "Generates all the Docker Hub tag variations for the given variant" - [variant] - (let [short-tag (:docker-tag variant) - full-tag (full-docker-tag variant) - base (into #{} [short-tag full-tag])] - (-> base - (conj - (docker-tag {:omit-jdk? true} variant) - (docker-tag {:omit-build-tool? true} variant) - (docker-tag {:omit-build-tool-version? true} variant) - (docker-tag {:omit-distro? true} variant) - (docker-tag {:omit-distro? true, :omit-build-tool-version? true} variant) - (docker-tag {:omit-jdk? true, :omit-build-tool-version? true} variant) - (docker-tag {:omit-jdk? true, :omit-distro? true - :omit-build-tool-version? true} variant)) - vec - sort))) + [docker-clojure.variant :as variant])) (defn variant->manifest [variant] @@ -28,19 +10,23 @@ (conj (remove nil? [(str/join " " (conj ["Tags:"] (->> variant - variant-tags + docker/all-tags (str/join ", ")))) (when-let [arch (:architectures variant)] - (str/join " " ["Architectures:" (str/join ", " arch)])) - (str/join " " ["Directory:" (df/build-dir variant)])]) + (str/join " " ["Architectures:" + (str/join ", " arch)])) + (str/join " " ["Directory:" + (df/build-dir variant)])]) nil))) (defn generate "Generates Docker manifest file for a given git commit and returns it as a string." [{:keys [maintainers architectures git-repo]} git-commit variants] - (let [maintainers-label "Maintainers:" - maintainers-sep (apply str ",\n" (repeat (inc (count maintainers-label)) " "))] + (let [merged-arch-variants (variant/merge-architectures architectures variants) + maintainers-label "Maintainers:" + maintainers-sep (apply str ",\n" (repeat (inc (count maintainers-label)) + " "))] (str/join "\n" (concat [(str/join " " [maintainers-label @@ -49,7 +35,6 @@ (str/join " " ["GitRepo:" git-repo]) (str/join " " ["GitCommit:" git-commit])] - (map variant->manifest variants) + (map variant->manifest merged-arch-variants) [nil])))) - diff --git a/src/docker_clojure/util.clj b/src/docker_clojure/util.clj index cbe128be..c3cd0468 100644 --- a/src/docker_clojure/util.clj +++ b/src/docker_clojure/util.clj @@ -1,56 +1,7 @@ (ns docker-clojure.util - (:require [docker-clojure.config :as cfg] - [docker-clojure.core :as-alias core] - [clojure.string :as str])) + (:require [docker-clojure.core :as-alias core])) (defn get-or-default "Returns the value in map m for key k or else the value for key :default." [m k] (get m k (get m :default))) - -(defn jdk-label - [omit-default? jdk-version base-image] - (if (and omit-default? (= cfg/default-jdk-version jdk-version) - (= (first (get-or-default cfg/base-images jdk-version)) - base-image)) - nil - (str - (case base-image - ("eclipse-temurin" "debian") "temurin" - base-image) - "-" jdk-version))) - -(defn docker-tag - "Returns the Docker tag for the given variant with truthy keys from first arg - left out when possible." - [{:keys [omit-all? omit-jdk? omit-build-tool? omit-build-tool-version? - omit-distro?]} - {:keys [base-image jdk-version distro build-tool - build-tool-version] :as _variant}] - (if (= ::core/all build-tool) - "latest" - (let [jdk (jdk-label (or omit-all? omit-jdk?) - jdk-version base-image) - dd (get-or-default cfg/default-distros jdk-version) - distro-label (if (and (or omit-all? omit-distro?) (= dd distro)) - nil - (when distro (name distro))) - tag-elements (remove nil? [jdk distro-label]) - build-tool-label (if (and (seq tag-elements) ; ensure tag is non-empty - (or omit-all? omit-build-tool?) - (= build-tool cfg/default-build-tool)) - nil - build-tool) - build-tool-version-label (if (or omit-all? omit-build-tool? - omit-build-tool-version?) - nil - build-tool-version)] - (str/join "-" (remove nil? [jdk build-tool-label - build-tool-version-label - distro-label]))))) - -(def full-docker-tag - (partial docker-tag {})) - -(def default-docker-tag - (partial docker-tag {:omit-jdk? true, :omit-distro? true})) diff --git a/src/docker_clojure/variant.clj b/src/docker_clojure/variant.clj new file mode 100644 index 00000000..045b2b56 --- /dev/null +++ b/src/docker_clojure/variant.clj @@ -0,0 +1,197 @@ +(ns docker-clojure.variant + (:refer-clojure :exclude [compare sort]) + (:require [clojure.math.combinatorics :as combo] + [clojure.spec.alpha :as s] + [clojure.spec.gen.alpha :as gen] + [clojure.string :as str] + [docker-clojure.config :as cfg] + [docker-clojure.core :as-alias core] + [docker-clojure.docker :as docker] + [docker-clojure.util :refer [get-or-default]])) + +(s/def ::variant-base + (s/keys :req-un [::cfg/jdk-version ::cfg/base-image ::cfg/base-image-tag + ::cfg/distro ::cfg/build-tool ::cfg/build-tool-version + ::cfg/maintainer ::cfg/docker-tag ::cfg/architecture] + :opt-un [::cfg/build-tool-versions])) + +(s/def ::variant + (s/with-gen + ::variant-base + #(gen/fmap (fn [[v btv]] + (if (= ::core/all (:build-tool v)) + (-> v ; ::core/all implies docker tag "latest" + (assoc :build-tool-version nil + :build-tool-versions btv) + (dissoc :distro :docker-tag :base-image-tag :base-image)) + v)) + (gen/tuple (s/gen ::variant-base) + (gen/map (s/gen ::cfg/specific-build-tool) + (s/gen ::cfg/specific-build-tool-version)))))) + +(s/def ::variants (s/coll-of ::variant)) + +(s/def ::manifest-variant + (s/keys :req-un [::cfg/jdk-version ::cfg/base-image ::cfg/base-image-tag + ::cfg/distro ::cfg/build-tool ::cfg/build-tool-version + ::cfg/maintainer ::cfg/docker-tag] + :opt-un [::cfg/build-tool-versions ::cfg/architectures])) + +(defn assoc-if + [m pred k v] + (if (pred) + (assoc m k v) + m)) + +(defn ->map [[base-image jdk-version distro + [build-tool build-tool-version] architecture]] + (let [base {:jdk-version jdk-version + :architecture architecture + :base-image base-image + :base-image-tag (docker/base-image-tag base-image jdk-version + distro) + :distro distro + :build-tool build-tool + :build-tool-version build-tool-version + :maintainer (str/join " & " cfg/maintainers)}] + (-> base + (assoc :docker-tag (docker/default-tag base)) + (assoc-if #(nil? (:build-tool-version base)) :build-tool-versions + cfg/build-tools)))) + +(s/def ::variant-tuple + (s/tuple ::cfg/base-image ::cfg/jdk-version ::cfg/distro + (s/tuple ::cfg/specific-build-tool ::cfg/specific-build-tool-version) + ::cfg/architecture)) + +(s/fdef ->map + :args (s/cat :variant-tuple ::variant-tuple) + :ret ::variant + :fn #(let [[base-image jdk-version distro + [specific-build-tool specific-build-tool-version] + architecture] (-> % :args :variant-tuple)] + (println "arg:" (-> % :args :variant-tuple pr-str)) + (println "ret:" (-> % :ret pr-str)) + (and (= (-> % :ret :base-image) base-image) + (= (-> % :ret :jdk-version) jdk-version) + (= (-> % :ret :distro) distro) + (= (-> % :ret :base-image-tag) + (docker/base-image-tag base-image jdk-version distro)) + (= (-> % :ret :build-tool last) specific-build-tool) + (= (-> % :ret :build-tool-version) specific-build-tool-version) + (= (-> % :ret :architecture) architecture)))) + +(defn exclude? + "Returns true if the map `variant` contains every key-value pair in the map + `exclusion`. `variant` may contain additional keys that are not in + `exclusion`. Some values of `exclusion` can also be a predicate of one + argument which is then tested against the respective value from `variant`. + Returns false if any of the keys in `exclusions` are missing from `variant` or + have different values, or the predicate value returned false." + [variant exclusion] + (every? (fn [[k v]] + (if (fn? v) + (v (get variant k)) + (= v (get variant k)))) + exclusion)) + +(defn compare + [v1 v2] + (let [c (clojure.core/compare (:jdk-version v1) (:jdk-version v2))] + (if (not= c 0) + c + (let [c (clojure.core/compare (docker/full-tag v1) + (docker/full-tag v2))] + (if (not= c 0) + c + (clojure.core/compare (:architecture v1) (:architecture v2))))))) + +(defn sort + [variants] + (clojure.core/sort + (fn [v1 v2] + (cond + (= "latest" (:docker-tag v1)) -1 + (= "latest" (:docker-tag v2)) 1 + :else (compare v1 v2))) + variants)) + +(defn equal? + [v1 v2] + (= 0 (compare v1 v2))) + +(defn equal-except-architecture? + [{arch1 :architecture :as v1} {arch2 :architecture :as v2}] + (and (not= arch1 arch2) + (equal? (dissoc v1 :architecture) (dissoc v2 :architecture)))) + +(defn combinations + [base-images jdk-versions distros build-tools architectures] + (reduce + (fn [variants jdk-version] + (concat + variants + (let [jdk-base-images (get-or-default base-images jdk-version)] + (loop [[bi & r] jdk-base-images + acc #{}] + (let [vs (combo/cartesian-product #{bi} #{jdk-version} + (get-or-default distros bi) + build-tools + architectures) + acc' (concat acc vs)] + (if (seq r) + (recur r acc') + acc')))))) + #{} jdk-versions)) + +(defn merge-architectures + [default-architectures variants] + (->> variants + (map #(assoc % :architectures #{(:architecture %)})) + (reduce + (fn [mav v] + (if-let [matching + (some #(when + (equal-except-architecture? v %) + %) + mav)] + (-> mav + (->> (remove #(= % matching))) + (conj (update matching :architectures conj + (:architecture v)))) + (conj mav v))) + []) + (map #(if (= (:architectures %) default-architectures) + (dissoc % :architectures :architecture) + (dissoc % :architecture))) + sort)) + +(s/fdef merge-architectures + :args (s/cat :default-architectures ::cfg/architectures + :variants + (s/with-gen + ::variants + #(gen/fmap + (fn [variants] + ;; duplicate variants for each architecture + (mapcat (fn [variant] + (map (fn [arch] + (assoc variant :architecture arch)) + cfg/architectures)) + variants)) + (s/gen (s/coll-of ::variant))))) + :ret (s/coll-of ::manifest-variant) + :fn #(let [ret-count (-> % :ret count) + arg-variants (-> % :args :variants) + should-merge? (fn [v] + (some (partial equal-except-architecture? v) + arg-variants)) + one-per-arch (fn [c] (if (> c 0) + (/ c (count cfg/architectures)) + c)) + num-should-merge (->> arg-variants + (filter should-merge?) + count + one-per-arch) + arg-count (- (count arg-variants) num-should-merge)] + (= ret-count arg-count))) diff --git a/test/docker_clojure/core_test.clj b/test/docker_clojure/core_test.clj index d98e2404..7792e3a4 100644 --- a/test/docker_clojure/core_test.clj +++ b/test/docker_clojure/core_test.clj @@ -1,9 +1,7 @@ (ns docker-clojure.core-test (:require [clojure.test :refer [deftest is are testing]] - [docker-clojure.core :refer [exclude? image-variants variant-map]] - [docker-clojure.config :as cfg] - [docker-clojure.util :refer [default-docker-tag]] - [clojure.string :as str])) + [docker-clojure.core :refer [exclude? image-variants]] + [docker-clojure.config :as cfg])) (deftest image-variants-test (testing "generates the expected set of variants" @@ -12,16 +10,18 @@ :default :ubuntu/noble} cfg/default-jdk-version 11 cfg/maintainers ["Paul Lam " - "Wes Morgan "]] + "Wes Morgan "] + cfg/architectures #{"amd64" "arm64v8"}] (let [variants (image-variants {8 ["debian"] 11 ["debian"] :default ["eclipse-temurin"]} #{8 11 17 18} {"debian" #{:debian/buster :debian-slim/buster-slim} - :default #{:alpine/alpine :ubuntu/noble}} + :default #{:alpine/alpine :ubuntu/noble}} {"lein" "2.9.1" - "tools-deps" "1.10.1.478"})] + "tools-deps" "1.10.1.478"} + #{"amd64" "arm64v8"})] ;; filter is to make failure output a little more humane (are [v] (contains? (->> variants (filter #(and (= (:base-image %) (:base-image v)) @@ -30,73 +30,130 @@ (= (:build-tool %) (:build-tool v)))) set) v) - {:jdk-version 11, :distro :debian-slim/buster-slim, :build-tool "lein" - :base-image "debian" :base-image-tag "debian:buster-slim" - :maintainer "Paul Lam & Wes Morgan " - :docker-tag "temurin-11-lein-2.9.1", :build-tool-version "2.9.1"} + {:jdk-version 11, :distro :debian-slim/buster-slim, :build-tool "lein" + :base-image "debian" :base-image-tag "debian:buster-slim" + :maintainer "Paul Lam & Wes Morgan " + :docker-tag "temurin-11-lein-2.9.1", :build-tool-version "2.9.1" + :architecture "amd64"} + {:jdk-version 11, :distro :debian-slim/buster-slim, :build-tool "lein" + :base-image "debian" :base-image-tag "debian:buster-slim" + :maintainer "Paul Lam & Wes Morgan " + :docker-tag "temurin-11-lein-2.9.1", :build-tool-version "2.9.1" + :architecture "arm64v8"} {:jdk-version 18, :distro :ubuntu/noble :base-image "eclipse-temurin" :base-image-tag "eclipse-temurin:18-jdk-noble" :build-tool "tools-deps" :maintainer "Paul Lam & Wes Morgan " :docker-tag "temurin-18-tools-deps-1.10.1.478" - :build-tool-version "1.10.1.478"} - {:jdk-version 11, :distro :debian/buster, :build-tool "lein" - :base-image "debian" :base-image-tag "debian:buster" - :maintainer "Paul Lam & Wes Morgan " - :docker-tag "temurin-11-lein-2.9.1-buster", :build-tool-version "2.9.1"} + :build-tool-version "1.10.1.478" + :architecture "amd64"} + {:jdk-version 18, :distro :ubuntu/noble + :base-image "eclipse-temurin" + :base-image-tag "eclipse-temurin:18-jdk-noble" + :build-tool "tools-deps" + :maintainer "Paul Lam & Wes Morgan " + :docker-tag "temurin-18-tools-deps-1.10.1.478" + :build-tool-version "1.10.1.478" + :architecture "arm64v8"} + {:jdk-version 11, :distro :debian/buster, :build-tool "lein" + :base-image "debian" :base-image-tag "debian:buster" + :maintainer "Paul Lam & Wes Morgan " + :docker-tag "temurin-11-lein-2.9.1-buster", :build-tool-version "2.9.1" + :architecture "amd64"} + {:jdk-version 11, :distro :debian/buster, :build-tool "lein" + :base-image "debian" :base-image-tag "debian:buster" + :maintainer "Paul Lam & Wes Morgan " + :docker-tag "temurin-11-lein-2.9.1-buster", :build-tool-version "2.9.1" + :architecture "arm64v8"} {:jdk-version 11, :distro :debian/buster :base-image "debian" :base-image-tag "debian:buster" :build-tool "tools-deps" :maintainer "Paul Lam & Wes Morgan " :docker-tag "temurin-11-tools-deps-1.10.1.478-buster" - :build-tool-version "1.10.1.478"} + :build-tool-version "1.10.1.478" + :architecture "amd64"} + {:jdk-version 11, :distro :debian/buster + :base-image "debian" + :base-image-tag "debian:buster" + :build-tool "tools-deps" + :maintainer "Paul Lam & Wes Morgan " + :docker-tag "temurin-11-tools-deps-1.10.1.478-buster" + :build-tool-version "1.10.1.478" + :architecture "arm64v8"} + {:jdk-version 8, :distro :debian-slim/buster-slim, :build-tool "lein" + :base-image "debian" + :base-image-tag "debian:buster-slim" + :maintainer "Paul Lam & Wes Morgan " + :docker-tag "temurin-8-lein-2.9.1", :build-tool-version "2.9.1" + :architecture "amd64"} {:jdk-version 8, :distro :debian-slim/buster-slim, :build-tool "lein" :base-image "debian" :base-image-tag "debian:buster-slim" :maintainer "Paul Lam & Wes Morgan " - :docker-tag "temurin-8-lein-2.9.1", :build-tool-version "2.9.1"} + :docker-tag "temurin-8-lein-2.9.1", :build-tool-version "2.9.1" + :architecture "arm64v8"} {:jdk-version 8, :distro :debian-slim/buster-slim :build-tool "tools-deps" :base-image "debian" :base-image-tag "debian:buster-slim" :maintainer "Paul Lam & Wes Morgan " :docker-tag "temurin-8-tools-deps-1.10.1.478" - :build-tool-version "1.10.1.478"} + :build-tool-version "1.10.1.478" + :architecture "amd64"} + {:jdk-version 8, :distro :debian-slim/buster-slim + :build-tool "tools-deps" + :base-image "debian" + :base-image-tag "debian:buster-slim" + :maintainer "Paul Lam & Wes Morgan " + :docker-tag "temurin-8-tools-deps-1.10.1.478" + :build-tool-version "1.10.1.478" + :architecture "arm64v8"} + {:jdk-version 17, :distro :ubuntu/noble, :build-tool "lein" + :base-image "eclipse-temurin" + :base-image-tag "eclipse-temurin:17-jdk-noble" + :maintainer "Paul Lam & Wes Morgan " + :docker-tag "temurin-17-lein-2.9.1" + :build-tool-version "2.9.1" + :architecture "amd64"} {:jdk-version 17, :distro :ubuntu/noble, :build-tool "lein" :base-image "eclipse-temurin" :base-image-tag "eclipse-temurin:17-jdk-noble" :maintainer "Paul Lam & Wes Morgan " :docker-tag "temurin-17-lein-2.9.1" - :build-tool-version "2.9.1"} + :build-tool-version "2.9.1" + :architecture "arm64v8"} + {:jdk-version 17, :distro :alpine/alpine, :build-tool "lein" + :base-image "eclipse-temurin" + :base-image-tag "eclipse-temurin:17-jdk-alpine" + :maintainer "Paul Lam & Wes Morgan " + :docker-tag "temurin-17-lein-2.9.1-alpine" + :build-tool-version "2.9.1" + :architecture "amd64"} {:jdk-version 17, :distro :alpine/alpine, :build-tool "lein" - :base-image "eclipse-temurin", :architectures #{"amd64"} + :base-image "eclipse-temurin" :base-image-tag "eclipse-temurin:17-jdk-alpine" :maintainer "Paul Lam & Wes Morgan " :docker-tag "temurin-17-lein-2.9.1-alpine" - :build-tool-version "2.9.1"} + :build-tool-version "2.9.1" + :architecture "arm64v8"} {:jdk-version 17, :distro :ubuntu/noble :base-image "eclipse-temurin" :base-image-tag "eclipse-temurin:17-jdk-noble" :build-tool "tools-deps" :maintainer "Paul Lam & Wes Morgan " :docker-tag "temurin-17-tools-deps-1.10.1.478" - :build-tool-version "1.10.1.478"}))))) - -(deftest variant-map-test - (testing "returns the expected map version of the image variant list" - (with-redefs [cfg/maintainers ["Paul Lam " - "Wes Morgan "]] - (is (= {:jdk-version 8 - :base-image "eclipse-temurin" - :base-image-tag "eclipse-temurin:8-jdk-distro" - :distro :distro/distro - :build-tool "build-tool" - :docker-tag "temurin-8-build-tool-1.2.3-distro" - :build-tool-version "1.2.3" - :maintainer "Paul Lam & Wes Morgan "} - (variant-map '("eclipse-temurin" 8 :distro/distro ["build-tool" "1.2.3"]))))))) + :build-tool-version "1.10.1.478" + :architecture "amd64"} + {:jdk-version 17, :distro :ubuntu/noble + :base-image "eclipse-temurin" + :base-image-tag "eclipse-temurin:17-jdk-noble" + :build-tool "tools-deps" + :maintainer "Paul Lam & Wes Morgan " + :docker-tag "temurin-17-tools-deps-1.10.1.478" + :build-tool-version "1.10.1.478" + :architecture "arm64v8"}))))) (deftest exclude?-test (testing "excludes variant that matches all key-values in any exclusion" @@ -107,31 +164,3 @@ (testing "does not exclude partial matches" (is (not (exclude? #{{:base-image "bad", :build-tool "woof"}} {:base-image "bad", :build-tool "lein"}))))) - -(deftest docker-tag-test - (with-redefs [cfg/default-jdk-version 11 ; TODO: Make this an arg to the fn instead - cfg/default-distros {:default :debian-slim/slim-buster}] ; TODO: Rethink this too? - (testing "default java version is left out" - (is (not (str/includes? (default-docker-tag {:jdk-version 11}) - "openjdk-11")))) - (testing "non-default version is added as a prefix" - (is (str/starts-with? (default-docker-tag {:base-image "openjdk" - :jdk-version 14}) - "openjdk-14"))) - (testing "default distro is left out" - (is (not (str/includes? (default-docker-tag {:jdk-version 14 - :distro :debian-slim/slim-buster}) - "slim-buster")))) - (testing "alpine is added as a suffix" - (is (str/ends-with? (default-docker-tag {:jdk-version 8 - :distro :alpine/alpine}) - "alpine"))) - (testing "build tool is included" - (is (str/includes? (default-docker-tag {:jdk-version 11 - :build-tool "lein"}) - "lein"))) - (testing "build tool version is included" - (is (str/includes? (default-docker-tag {:jdk-version 11 - :build-tool "lein" - :build-tool-version "2.11.2"}) - "2.11.2"))))) diff --git a/test/docker_clojure/docker_test.clj b/test/docker_clojure/docker_test.clj new file mode 100644 index 00000000..583ae08b --- /dev/null +++ b/test/docker_clojure/docker_test.clj @@ -0,0 +1,69 @@ +(ns docker-clojure.docker-test + (:require [clojure.string :as str] + [clojure.test :refer :all] + [docker-clojure.config :as cfg] + [docker-clojure.docker :refer :all])) + +(deftest tag-test + (with-redefs [cfg/default-jdk-version 11 ; TODO: Make this an arg to the fn instead + cfg/default-distros {:default :debian-slim/slim-buster}] ; TODO: Rethink this too? + (testing "default java version is left out" + (is (not (str/includes? (default-tag {:jdk-version 11}) + "openjdk-11")))) + (testing "non-default version is added as a prefix" + (is (str/starts-with? (default-tag {:base-image "openjdk" + :jdk-version 14}) + "openjdk-14"))) + (testing "default distro is left out" + (is (not (str/includes? (default-tag {:jdk-version 14 + :distro :debian-slim/slim-buster}) + "slim-buster")))) + (testing "alpine is added as a suffix" + (is (str/ends-with? (default-tag {:jdk-version 8 + :distro :alpine/alpine}) + "alpine"))) + (testing "build tool is included" + (is (str/includes? (default-tag {:jdk-version 11 + :build-tool "lein"}) + "lein"))) + (testing "build tool version is included" + (is (str/includes? (default-tag {:jdk-version 11 + :build-tool "lein" + :build-tool-version "2.11.2"}) + "2.11.2"))))) + +(deftest all-tags-test + (testing "Generates all-defaults tag for a build tool" + (let [tags (all-tags {:base-image "debian" + :jdk-version 21 + :distro :debian/bookworm + :build-tool "tools-deps" + :build-tool-version "1.11.1.1155"})] + (is ((set tags) "tools-deps")))) + (testing "Generates jdk-version-build-tool tag for every jdk version" + (are [jdk-version tag] + (let [tags (all-tags {:base-image (if (< jdk-version 21) + "eclipse-temurin" + "debian") + :jdk-version jdk-version + :distro (if (< jdk-version 21) + :ubuntu/jammy + :debian/bookworm) + :build-tool "tools-deps" + :build-tool-version "1.11.1.1155"})] + ((set tags) tag)) + 11 "temurin-11-tools-deps" + 17 "temurin-17-tools-deps" + 21 "temurin-21-tools-deps")) + (testing "Generates build-tool-distro tag for every distro" + (are [distro tag] + (let [tags (all-tags {:base-image "debian" + :jdk-version 21 + :distro distro + :build-tool "tools-deps" + :build-tool-version "1.11.1.1155"})] + ((set tags) tag)) + :debian/bullseye "tools-deps-bullseye" + :debian-slim/bullseye-slim "tools-deps-bullseye-slim" + :debian/bookworm "tools-deps-bookworm" + :debian-slim/bookworm-slim "tools-deps-bookworm-slim"))) diff --git a/test/docker_clojure/fix_kaocha.clj b/test/docker_clojure/fix_kaocha.clj new file mode 100644 index 00000000..cf10ffc5 --- /dev/null +++ b/test/docker_clojure/fix_kaocha.clj @@ -0,0 +1,15 @@ +(ns docker-clojure.fix-kaocha + "Kaocha often has bugs with tools-deps exec-fn usage because they have two + different entry points (and one doesn't use the other). They insist on + everyone using a `bin/kaocha` script to run their tests even though that is + inconsistent with most Clojure tooling idioms. This ns exists to work around + bugs we encounter and make kaocha behave better." + (:require [clojure.spec.alpha :as spec] + [expound.alpha :as expound] + [kaocha.runner :as kaocha])) + +(defn run-tests + [opts] + ;; work around https://github.com/lambdaisland/kaocha/issues/445 + (binding [spec/*explain-out* expound/printer] + (kaocha/exec-fn opts))) diff --git a/test/docker_clojure/manifest_test.clj b/test/docker_clojure/manifest_test.clj index 30303036..493b9eaa 100644 --- a/test/docker_clojure/manifest_test.clj +++ b/test/docker_clojure/manifest_test.clj @@ -1,39 +1,15 @@ (ns docker-clojure.manifest-test (:require [clojure.test :refer [deftest is are testing]] - [docker-clojure.manifest :refer [variant-tags]])) + [docker-clojure.manifest :refer [variant->manifest]])) -(deftest variant-tags-test - (testing "Generates all-defaults tag for a build tool" - (let [tags (variant-tags {:base-image "debian" - :jdk-version 21 - :distro :debian/bookworm - :build-tool "tools-deps" - :build-tool-version "1.11.1.1155"})] - (is ((set tags) "tools-deps")))) - (testing "Generates jdk-version-build-tool tag for every jdk version" - (are [jdk-version tag] - (let [tags (variant-tags {:base-image (if (< jdk-version 21) - "eclipse-temurin" - "debian") - :jdk-version jdk-version - :distro (if (< jdk-version 21) - :ubuntu/jammy - :debian/bookworm) - :build-tool "tools-deps" - :build-tool-version "1.11.1.1155"})] - ((set tags) tag)) - 11 "temurin-11-tools-deps" - 17 "temurin-17-tools-deps" - 21 "temurin-21-tools-deps")) - (testing "Generates build-tool-distro tag for every distro" - (are [distro tag] - (let [tags (variant-tags {:base-image "debian" - :jdk-version 21 - :distro distro - :build-tool "tools-deps" - :build-tool-version "1.11.1.1155"})] - ((set tags) tag)) - :debian/bullseye "tools-deps-bullseye" - :debian-slim/bullseye-slim "tools-deps-bullseye-slim" - :debian/bookworm "tools-deps-bookworm" - :debian-slim/bookworm-slim "tools-deps-bookworm-slim"))) +(deftest variant->manifest-test + (testing "generates the correct manifest text for a variant" + (is (= "\nTags: temurin-17-noble, temurin-17-tools-deps-1.10.1.478, temurin-17-tools-deps-1.10.1.478-noble, temurin-17-tools-deps-noble\nDirectory: target/eclipse-temurin-17-jdk-noble/tools-deps" + (variant->manifest + {:jdk-version 17, :distro :ubuntu/noble + :base-image "eclipse-temurin" + :base-image-tag "eclipse-temurin:17-jdk-noble" + :build-tool "tools-deps" + :maintainer "Paul Lam

& Wes Morgan " + :docker-tag "temurin-17-tools-deps-1.10.1.478" + :build-tool-version "1.10.1.478"}))))) diff --git a/test/docker_clojure/test_runner.clj b/test/docker_clojure/test_runner.clj deleted file mode 100644 index 7b698930..00000000 --- a/test/docker_clojure/test_runner.clj +++ /dev/null @@ -1,11 +0,0 @@ -(ns docker-clojure.test-runner - (:require [clojure.test :refer [run-tests]]) - (:gen-class)) - -(defn -main [& namespaces] - (doseq [ns namespaces] - (require ns)) - (let [test-results (apply run-tests namespaces) - {:keys [fail error]} test-results - failures-and-errors (+ fail error)] - (System/exit failures-and-errors))) diff --git a/test/docker_clojure/variant_test.clj b/test/docker_clojure/variant_test.clj new file mode 100644 index 00000000..8134dd72 --- /dev/null +++ b/test/docker_clojure/variant_test.clj @@ -0,0 +1,65 @@ +(ns docker-clojure.variant-test + (:refer-clojure :exclude [compare sort]) + (:require [clojure.string :as str] + [clojure.test :refer :all] + [docker-clojure.config :as cfg] + [docker-clojure.core :as-alias core] + [docker-clojure.variant :refer :all])) + +(deftest ->map-test + (testing "returns the expected map version of the image variant list" + (with-redefs [cfg/maintainers ["Paul Lam " + "Wes Morgan "]] + (is (= {:jdk-version 8 + :base-image "eclipse-temurin" + :base-image-tag "eclipse-temurin:8-jdk-distro" + :distro :distro/distro + :build-tool "build-tool" + :docker-tag "temurin-8-build-tool-1.2.3-distro" + :build-tool-version "1.2.3" + :maintainer "Paul Lam & Wes Morgan " + :architecture "i386"} + (->map '("eclipse-temurin" 8 :distro/distro + ["build-tool" "1.2.3"] "i386")))) + + (is (= {:jdk-version 22 + :base-image "debian" + :base-image-tag "debian:bookworm" + :distro :debian/bookworm + :build-tool "tools-deps" + :docker-tag "temurin-22-tools-deps-1.12.0.1530" + :build-tool-version "1.12.0.1530" + :maintainer "Paul Lam & Wes Morgan " + :architecture "arm64"} + (->map '("debian" 22 :debian/bookworm + ["tools-deps" "1.12.0.1530"] "arm64")))) + + (is (= {:jdk-version 22 + :base-image "debian" + :base-image-tag "debian:bookworm" + :distro :debian/bookworm + :build-tool ::core/all + :docker-tag "latest" + :build-tool-version nil + :build-tool-versions cfg/build-tools + :maintainer "Paul Lam & Wes Morgan " + :architecture "arm64"} + (->map '("debian" 22 :debian/bookworm + [::core/all] "arm64"))))))) + +(deftest exclude?-test + (testing "gets excluded if contains every key-value pair in exclusion" + (is (exclude? {:foo "bar" :baz "qux" :other "thingy"} + {:foo "bar" :baz "qux"}))) + (testing "gets excluded if pred fn returns true" + (is (exclude? {:foo "bar" :baz "qux" :other "thingy"} + {:foo #(str/starts-with? % "b") :baz "qux"}))) + (testing "remains included if missing one key-value pair from exclusion" + (is (not (exclude? {:foo "bar" :other "thingy"} + {:foo "bar" :baz "qux"})))) + (testing "remains included if key's value doesn't match exclusion's" + (is (not (exclude? {:foo "burp" :baz "qux" :other "thingy"} + {:foo "bar" :baz "qux"})))) + (testing "remains included if pred fn returns false" + (is (not (exclude? {:foo "bar" :baz "qux" :other "thingy"} + {:foo "bar" :baz #(> (count %) 3)}))))) diff --git a/tests.edn b/tests.edn new file mode 100644 index 00000000..e4b7137b --- /dev/null +++ b/tests.edn @@ -0,0 +1 @@ +#kaocha/v1 {:plugins [:kaocha.plugin.alpha/spec-test-check]}