diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml index be32041..06e8908 100644 --- a/.github/workflows/ci-cd.yaml +++ b/.github/workflows/ci-cd.yaml @@ -20,8 +20,6 @@ env: OPTIMIZATION_LEVEL: '-O2' POD_ID: pod.jackdbd.jsoup POD_NAME: pod-jackdbd-jsoup - # TODO: maybe create a snapshot build when the branch is not main (e.g. canary) - SNAPSHOT: "false" jobs: build-uberjar: @@ -34,11 +32,24 @@ jobs: outputs: pod_version: ${{ steps.set_pod_version.outputs.pod_version }} + snapshot_suffix: ${{ steps.set_snapshot_suffix.outputs.snapshot_suffix }} steps: - name: 🛎️ Checkout uses: actions/checkout@v4 + - name: Set SNAPSHOT_SUFFIX (environment variable) and snapshot_suffix (job output) + id: set_snapshot_suffix + # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables + run: | + if [ "${{ github.ref }}" != "refs/heads/main" ]; then + echo "SNAPSHOT_SUFFIX=${GITHUB_SHA}" >> $GITHUB_ENV + else + echo "SNAPSHOT_SUFFIX=" >> $GITHUB_ENV + fi + echo "snapshot_suffix=$SNAPSHOT_SUFFIX" >> $GITHUB_OUTPUT + echo "SNAPSHOT_SUFFIX is $SNAPSHOT_SUFFIX" + # https://github.com/marketplace/actions/github-action-for-graalvm - name: 🔧 Setup GraalVM uses: graalvm/setup-graalvm@v1 @@ -70,26 +81,30 @@ jobs: key: cljdeps-${{ hashFiles('deps.edn', 'bb.edn') }} restore-keys: cljdeps- - # TODO: At the moment POD_VERSION is correct only for "standard" builds, - # but not for "snapshot" builds. - # Problem: In build.clj I am using the clojure.tools.build.api/git-count-revs - # function to generate the snapshot version tag. But that only works in - # Clojure, not Babashka. - # Solution: I should propably replace it with the same function from this fork: - # https://github.com/babashka/tools.bbuild # Example of an uberjar of a standard release: - # pod.jackdbd.jsoup-0.1.1-standalone.jar + # pod.jackdbd.jsoup-0.1.2-standalone.jar # Example of an uberjar of a snapshot release: - # pod.jackdbd.jsoup-0.1.1-21-SNAPSHOT-standalone.jar + # pod.jackdbd.jsoup-0.1.2-345-SNAPSHOT-standalone.jar - name: Set POD_VERSION (environment variable) and pod_version (job output) id: set_pod_version run: | - POD_VERSION=$(bb -e '(-> (slurp "deps.edn") edn/read-string :aliases :neil :project :version)' | tr -d '"') + PROJECT_VERSION=$(bb -e '(-> (slurp "deps.edn") edn/read-string :aliases :neil :project :version)' | tr -d '"') + if [ -z "${SNAPSHOT_SUFFIX}" ]; then + POD_VERSION=$PROJECT_VERSION + else + POD_VERSION="${PROJECT_VERSION}-${SNAPSHOT_SUFFIX}" + fi echo "POD_VERSION=$POD_VERSION" >> $GITHUB_ENV echo "pod_version=$POD_VERSION" >> $GITHUB_OUTPUT + echo "POD_VERSION is $POD_VERSION" - name: 📦 Compile pod into an uberjar - run: clojure -T:build uber :snapshot ${{ env.SNAPSHOT }} + run: | + if [ -n "${SNAPSHOT_SUFFIX}" ]; then + clojure -T:build uber :snapshot-suffix ${{ env.SNAPSHOT_SUFFIX }} + else + clojure -T:build uber + fi - name: ⬆️ Upload uberjar uses: actions/upload-artifact@v4 @@ -104,6 +119,7 @@ jobs: env: POD_VERSION: ${{ needs.build-uberjar.outputs.pod_version }} + SNAPSHOT_SUFFIX: ${{ needs.build-uberjar.outputs.snapshot_suffix }} steps: - name: 🛎️ Checkout @@ -332,6 +348,7 @@ jobs: env: POD_VERSION: ${{ needs.build-uberjar.outputs.pod_version }} + SNAPSHOT_SUFFIX: ${{ needs.build-uberjar.outputs.snapshot_suffix }} permissions: contents: write @@ -346,23 +363,18 @@ jobs: cli: 'latest' bb: 'latest' - # - name: ⬇️ Download uberjar - # uses: actions/download-artifact@v4 - # with: - # name: uberjar - - # - name: Copy the uberjar to the target directory - # run: | - # mkdir target - # cp ${{ env.POD_ID }}-${{ env.POD_VERSION }}-standalone.jar target/${{ env.POD_ID }}-${{ env.POD_VERSION }}-standalone.jar - - name: 🌐 Publish to Clojars env: CLOJARS_USERNAME: jackdbd CLOJARS_PASSWORD: ${{ secrets.CLOJARS_PASSWORD }} run: | - clojure -T:build uber :snapshot ${{ env.SNAPSHOT }} - clojure -T:build deploy :snapshot ${{ env.SNAPSHOT }} + if [ -n "${SNAPSHOT_SUFFIX}" ]; then + clojure -T:build uber :snapshot-suffix ${{ env.SNAPSHOT_SUFFIX }} + clojure -T:build deploy :snapshot-suffix ${{ env.SNAPSHOT_SUFFIX }} + else + clojure -T:build uber + clojure -T:build deploy + fi - name: ⬇️ Download x86_64 Linux binary uses: actions/download-artifact@v4 diff --git a/README.md b/README.md index ee08d9e..626b644 100644 --- a/README.md +++ b/README.md @@ -5,37 +5,19 @@ Babashka pod for parsing HTML with [jsoup](https://jsoup.org/). -## Setup +## How to use it? -The developer environment for this project is declared using [devenv](https://github.com/cachix/devenv). - -This project is managed with [neil](https://github.com/babashka/neil) and [Babashka tasks](https://book.babashka.org/#tasks). - -## Compile the pod - -### JAR - -```sh -clj -T:build jar -``` - -### Uber-JAR +See [examples/jsoup.bb](./examples/jsoup.bb). -```sh -clj -T:build uber -``` +## Development -### Executable binary +The developer environment for this project is declared using [devenv](https://github.com/cachix/devenv). -If you are on Linux, you can compile a statically-linked binary with the following command, which uses GraalVM native-image with [musl](https://musl.libc.org/) support. +This project is managed with [neil](https://github.com/babashka/neil) and [Babashka tasks](https://book.babashka.org/#tasks). You can use `bb tasks` to view all available tasks. -```sh -clj -T:build uber && ./script/compile.sh - -# or, in alternative, just run the following command: -bb build:native -``` +### Linux binary +If you are on Linux, you can compile a statically-linked binary using `bb build:binary`. Double check that the binary is statically linked. ```sh @@ -43,15 +25,7 @@ ldd target/pod-jackdbd-jsoup objdump --dynamic-syms target/pod-jackdbd ``` -## Tests - -Run all tests - -```sh -clj -X:test -``` - -## Upgrade version +### Upgrade version Use `neil` to update the version in `deps.edn`. Here are a few examples: @@ -61,7 +35,7 @@ neil version patch neil version minor ``` -These `neil` commands: +A few things to keep in mind about `neil version`: -- Create a Git commit and tag (this can be bypassed with `--no-tag`) -- Require the working directory to be clean (this can be bypassed with `--force`) +- it creates a Git commit and tag (this can be bypassed with `--no-tag`) +- it requires the working directory to be clean (this can be bypassed with `--force`) diff --git a/bb.edn b/bb.edn index d2c502c..9ae4d7c 100644 --- a/bb.edn +++ b/bb.edn @@ -6,21 +6,23 @@ build:binary {:doc "Compile the pod into a statically-linked binary with GraalVM native-image (Linux only)" - :depends [build:uberjar] + :depends [build:uber] :task (shell "script/compile.sh")} build:jar {:doc "Compile the the pod into a jar" :depends [clean] - :task (let [snapshot (System/getenv "SNAPSHOT")] - (clojure "-T:build jar :snapshot" snapshot))} + :task (if-let [snapshot-suffix (System/getenv "SNAPSHOT_SUFFIX")] + (clojure "-T:build jar :snapshot-suffix" (keyword snapshot-suffix)) + (clojure "-T:build jar"))} - build:uberjar + build:uber {:doc "Compile the the pod into an uberjar" :depends [clean] :task - (let [snapshot (System/getenv "SNAPSHOT")] - (clojure "-T:build uber :snapshot" snapshot))} + (if-let [snapshot-suffix (System/getenv "SNAPSHOT_SUFFIX")] + (clojure "-T:build uber :snapshot-suffix" (keyword snapshot-suffix)) + (clojure "-T:build uber"))} clean {:doc "Clean all compilation artifacts (e.g. jars, binaries)" @@ -32,10 +34,11 @@ deploy:clojars {:doc "Deploy the pod to Clojars" - :depends [build:uberjar] + :depends [build:uber] :task - (let [snapshot (System/getenv "SNAPSHOT")] - (clojure "-T:build deploy :snapshot" snapshot))} + (if-let [snapshot-suffix (System/getenv "SNAPSHOT_SUFFIX")] + (clojure "-T:build deploy :snapshot-suffix" (keyword snapshot-suffix)) + (clojure "-T:build deploy"))} deps:audit {:doc "Audit all dependencies declared in deps.edn" @@ -52,7 +55,7 @@ list:uberjar {:doc "List the contents of the uberjar" - :depends [build:uberjar] + :depends [build:uber] :task (let [project (-> (edn/read-string (slurp "deps.edn")) :aliases :neil :project) pod-name "pod.jackdbd.jsoup" pod-version (:version project) diff --git a/build.clj b/build.clj index 17e7486..0bef997 100644 --- a/build.clj +++ b/build.clj @@ -2,24 +2,24 @@ "pod.jackdbd.jsoup's build script. clojure -T:build jar - clojure -T:build jar :snapshot true clojure -T:build uber - clojure -T:build uber :snapshot true + clojure -T:build uber :snapshot-prefix :123-SNAPSHOT clojure -T:build deploy - clojure -T:build deploy :snapshot true + clojure -T:build deploy :snapshot-prefix :123-SNAPSHOT For more information, run: - - clojure -A:deps -T:build help/doc" + + clojure -A:deps -T:build help/doc" (:require [clojure.tools.build.api :as b] [clojure.edn :as edn] + [clojure.pprint :refer [pprint]] [deps-deploy.deps-deploy :as dd])) +;; The clojure.tools.build.api library works only in Clojure, not Babashka. +;; Consider replacing it with this fork https://github.com/babashka/tools.bbuild + (def project (-> (edn/read-string (slurp "deps.edn")) :aliases :neil :project)) (def lib (:name project)) -(def release-version (:version project)) -;; https://stackoverflow.com/a/5901460/3036129 -(def snapshot-version (format "%s-%s-%s" release-version (b/git-count-revs nil) "SNAPSHOT")) ;; https://clojure.github.io/tools.build/clojure.tools.build.api.html#var-write-pom (defn- pom-template [{:keys [version]}] @@ -39,8 +39,12 @@ [:developerConnection "scm:git:ssh:git@github.com:jackdbd/pod-jackdbd-jsoup.git"] [:tag (str "v" version)]]]) +;; What exactly is a Maven Snapshot and why do we need it? +;; https://stackoverflow.com/a/5901460/3036129 (defn- shared-config [opts] - (let [vers (if (:snapshot opts) snapshot-version release-version)] + (let [vers (if (:snapshot-suffix opts) + (format "%s-%s" (:version project) (name (:snapshot-suffix opts))) + (:version project))] (assoc opts :basis (b/create-basis {:project "deps.edn"}) :class-dir "target/classes" @@ -53,12 +57,13 @@ :uber-file (format "target/%s-%s-standalone.jar" (name lib) vers) :version vers))) -(defn clean [_] +(defn clean "Remove all compilation artifacts." [_] (b/delete {:path "target"})) (defn jar "Build the JAR." [opts] (let [config (shared-config opts) {:keys [basis class-dir jar-file lib main pom-data src-dirs target version]} config] + (clean nil) ;; https://clojure.github.io/tools.build/clojure.tools.build.api.html#var-write-pom @@ -83,6 +88,7 @@ (defn uber "Build the uber-JAR." [opts] (let [config (shared-config opts) {:keys [basis class-dir main pom-data src-dirs target uber-file version]} config] + (clean nil) (println "\nWriting" (b/pom-path (select-keys config [:lib :class-dir])) "...") @@ -107,10 +113,17 @@ :uber-file uber-file}))) (defn deploy "Deploy the uberjar to Clojars." [opts] + (println "\nOptions") + (pprint opts) + (let [config (shared-config opts) {:keys [uber-file]} config artifact (b/resolve-path uber-file) pom-file (b/pom-path (select-keys config [:lib :class-dir]))] + + (println "\nConfig") + (pprint config) + ;; https://github.com/slipset/deps-deploy/blob/master/doc/intro.md (dd/deploy {:artifact artifact :installer :remote @@ -118,8 +131,11 @@ opts) (comment - (prn (pom-template {:version release-version})) + (prn (pom-template {:version (:version project)})) + + (def snapshot-version (format "%s-%s" (:version project) (name (:snapshot-suffix "123-SNAPSHOT")))) (prn (pom-template {:version snapshot-version})) + (jar {}) (uber {}) - (deploy {})) + (deploy {:snapshot-prefix :123-SNAPSHOT})) diff --git a/devenv.nix b/devenv.nix index cf500b2..c98ca8e 100644 --- a/devenv.nix +++ b/devenv.nix @@ -1,6 +1,13 @@ {pkgs, ...}: { enterShell = '' versions + + # When SNAPSHOT_SUFFIX is set, `bb deploy:clojars` will create/overwrite a + # snapshot release. When SNAPSHOT_SUFFIX is not set, or set to an empty + # string, `bb deploy:clojars` will create a regular release, or fail if the + # release already exists. + export SNAPSHOT_SUFFIX="$(git rev-list --count HEAD)-SNAPSHOT" + # export SNAPSHOT_SUFFIX="" ''; enterTest = '' @@ -34,7 +41,6 @@ clojars_secrets.deploy_token; GH_TOKEN = builtins.readFile "/run/secrets/github-tokens/semantic_release_bot"; - SNAPSHOT = "true"; # this must be either "true" or "false", not true or false }; languages = { diff --git a/script/compile.sh b/script/compile.sh index 6c6b959..67920f6 100755 --- a/script/compile.sh +++ b/script/compile.sh @@ -3,7 +3,18 @@ set -euo pipefail POD_ID=pod.jackdbd.jsoup POD_NAME=pod-jackdbd-jsoup -POD_VERSION=$(bb -e '(-> (slurp "deps.edn") edn/read-string :aliases :neil :project :version)' | tr -d '"') +PROJECT_VERSION=$(bb -e '(-> (slurp "deps.edn") edn/read-string :aliases :neil :project :version)' | tr -d '"') + +# If we built an uberjar for a snapshot release, its filename will include a +# snapshot suffix. +if [ -z "${SNAPSHOT_SUFFIX}" ]; then + POD_VERSION=$PROJECT_VERSION +else + POD_VERSION="${PROJECT_VERSION}-${SNAPSHOT_SUFFIX}" +fi +echo "PROJECT_VERSION is $PROJECT_VERSION" +echo "SNAPSHOT_SUFFIX is $SNAPSHOT_SUFFIX" +echo "POD_VERSION is $POD_VERSION" # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables if [ "${CI+x}" ]; then