From b426b8f17cc1de79b3bf120e4aba29fdcf842a8e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 24 Jul 2026 04:23:21 +0000 Subject: [PATCH] ci(publish): ship per-classifier fat jars on GitHub, drop fat jar from Central MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The srcmorph-cli fat jar (jar-with-dependencies) was attached and deployed to Maven Central on every release (a 74 MB artifact bundling the net.ladenthin:llama native). A jar-with-dependencies on Central is redundant — consumers depend on the plain jar + its resolved graph — and it only ever carried the default CPU native, so there was no GPU-accelerated variant anywhere. - srcmorph-cli/pom.xml: set false on the assembly execution, so the fat jar is still built into target/ but is neither installed to ~/.m2 nor deployed to Central. - publish.yml (publish-release + publish-snapshot): collect the deploy-signed plain/sources/javadoc jars first (excluding the now-unsigned fat jar), then build one srcmorph-cli fat jar per net.ladenthin:llama native classifier (the full set from java-llama.cpp, all published on Central) plus the default all-platform CPU fat jar, GPG-sign each, and attach them to the GitHub Release. The default CPU build runs last so the unsuffixed jar is the run-anywhere CPU variant; each classifier variant is named srcmorph-cli--jar-with-dependencies-.jar and runs only on its own OS/arch/backend. - srcmorph-cli/README.md: document that the fat jars live on GitHub Releases (with .asc), the CPU default vs the per-backend GPU classifier variants. Verified locally: attach=false still produces the fat jar; the default build is 74 MB (all CPU natives) and a vulkan-linux-x86-64 build is 25 MB (single backend), confirming the classifier override bundles the right native. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01TJzCezSnQ8FxpFdeVxYxQY --- .github/workflows/publish.yml | 114 +++++++++++++++++++++++++++++----- srcmorph-cli/README.md | 14 ++++- srcmorph-cli/pom.xml | 15 ++++- 3 files changed, 124 insertions(+), 19 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 720f8ae..e72388f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -461,17 +461,58 @@ jobs: # job *after* the bundle was uploaded (and typically published server-side), while # the signed jars + .asc files already exist in each module's target/ (signing happens at # verify). Collecting on failure lets the github-snapshot job still attach them. - - name: Collect signed artifacts + # NOTE: the fat jar (jar-with-dependencies) is EXCLUDED here — it is no longer attached/ + # deployed to Central (srcmorph-cli/pom.xml sets false), so it is unsigned + # in target/ at this point. It is built + signed + collected by the next step instead. This + # step runs BEFORE that one so the deploy-signed plain/sources/javadoc jars are captured + # before the classifier loop rebuilds (and clobbers) srcmorph-cli/target. + - name: Collect signed Central artifacts (plain/sources/javadoc; fat jars handled next) if: ${{ !cancelled() }} - # All three reactor modules produce their own jars/asc files (root pom has no jar). run: | mkdir -p signed-snapshot-assets - cp srcmorph/target/*.jar signed-snapshot-assets/ 2>/dev/null || true - cp srcmorph/target/*.jar.asc signed-snapshot-assets/ 2>/dev/null || true - cp srcmorph-cli/target/*.jar signed-snapshot-assets/ 2>/dev/null || true - cp srcmorph-cli/target/*.jar.asc signed-snapshot-assets/ 2>/dev/null || true - cp srcmorph-maven-plugin/target/*.jar signed-snapshot-assets/ 2>/dev/null || true - cp srcmorph-maven-plugin/target/*.jar.asc signed-snapshot-assets/ 2>/dev/null || true + for m in srcmorph srcmorph-cli srcmorph-maven-plugin; do + for f in "$m"/target/*.jar "$m"/target/*.jar.asc; do + [ -e "$f" ] || continue + case "$f" in *-jar-with-dependencies.jar|*-jar-with-dependencies.jar.asc) continue ;; esac + cp "$f" signed-snapshot-assets/ + done + done + # Build one srcmorph-cli fat jar per net.ladenthin:llama native classifier + the default + # all-platform CPU fat jar, GPG-sign each, and add them to the GitHub-Release assets. These + # are GitHub-only (never Maven Central): each bundles exactly one llama native binary, so a + # GPU jar runs only on its own OS/arch/backend, while the default (no classifier) bundles the + # multi-platform CPU natives and runs anywhere. Signing uses the key setup-java already + # imported (armored detached .asc, same shape Central produces). + - name: Build & sign classifier fat jars (GitHub-only assets, never Central) + if: ${{ !cancelled() }} + env: + MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + run: | + set -euo pipefail + if [ -n "${MAVEN_GPG_PASSPHRASE:-}" ]; then echo "::add-mask::${MAVEN_GPG_PASSPHRASE}"; fi + # Source of truth: the set in java-llama.cpp llama/pom.xml for the pinned + # ${llama.version} (all published to Maven Central). Keep in sync on a llama bump. + CLASSIFIERS=( + cuda13-linux-x86-64 cuda13-windows-x86-64 + vulkan-linux-x86-64 vulkan-linux-aarch64 vulkan-windows-x86-64 + opencl-windows-x86-64 opencl-windows-aarch64 opencl-android-aarch64 + rocm-linux-x86-64 rocm-windows-x86-64 + sycl-fp16-linux-x86-64 sycl-fp32-linux-x86-64 sycl-windows-x86-64 + openvino-linux-x86-64 openvino-windows-x86-64 + msvc-windows + ) + build() { mvn -B --no-transfer-progress -pl srcmorph-cli -am -DskipTests -Dmaven.javadoc.skip=true -Dllama.classifier="$1" package; } + for c in "${CLASSIFIERS[@]}"; do + build "$c" + f=$(ls srcmorph-cli/target/srcmorph-cli-*-jar-with-dependencies.jar) + cp "$f" "${f%.jar}-${c}.jar" + done + # default CPU build LAST so the unsuffixed fat jar is the all-platform CPU variant + build "" + for f in srcmorph-cli/target/srcmorph-cli-*-jar-with-dependencies*.jar; do + printf '%s' "$MAVEN_GPG_PASSPHRASE" | gpg --batch --yes --pinentry-mode loopback --passphrase-fd 0 --detach-sign --armor "$f" + cp "$f" "$f".asc signed-snapshot-assets/ + done - uses: actions/upload-artifact@v7 if: ${{ !cancelled() }} with: @@ -542,17 +583,58 @@ jobs: # job *after* the bundle was uploaded (and typically published server-side), while # the signed jars + .asc files already exist in each module's target/ (signing happens at # verify). Collecting on failure lets the github-release job still attach them. - - name: Collect signed artifacts + # NOTE: the fat jar (jar-with-dependencies) is EXCLUDED here — it is no longer attached/ + # deployed to Central (srcmorph-cli/pom.xml sets false), so it is unsigned + # in target/ at this point. It is built + signed + collected by the next step instead. This + # step runs BEFORE that one so the deploy-signed plain/sources/javadoc jars are captured + # before the classifier loop rebuilds (and clobbers) srcmorph-cli/target. + - name: Collect signed Central artifacts (plain/sources/javadoc; fat jars handled next) if: ${{ !cancelled() }} - # All three reactor modules produce their own jars/asc files (root pom has no jar). run: | mkdir -p signed-release-assets - cp srcmorph/target/*.jar signed-release-assets/ 2>/dev/null || true - cp srcmorph/target/*.jar.asc signed-release-assets/ 2>/dev/null || true - cp srcmorph-cli/target/*.jar signed-release-assets/ 2>/dev/null || true - cp srcmorph-cli/target/*.jar.asc signed-release-assets/ 2>/dev/null || true - cp srcmorph-maven-plugin/target/*.jar signed-release-assets/ 2>/dev/null || true - cp srcmorph-maven-plugin/target/*.jar.asc signed-release-assets/ 2>/dev/null || true + for m in srcmorph srcmorph-cli srcmorph-maven-plugin; do + for f in "$m"/target/*.jar "$m"/target/*.jar.asc; do + [ -e "$f" ] || continue + case "$f" in *-jar-with-dependencies.jar|*-jar-with-dependencies.jar.asc) continue ;; esac + cp "$f" signed-release-assets/ + done + done + # Build one srcmorph-cli fat jar per net.ladenthin:llama native classifier + the default + # all-platform CPU fat jar, GPG-sign each, and add them to the GitHub-Release assets. These + # are GitHub-only (never Maven Central): each bundles exactly one llama native binary, so a + # GPU jar runs only on its own OS/arch/backend, while the default (no classifier) bundles the + # multi-platform CPU natives and runs anywhere. Signing uses the key setup-java already + # imported (armored detached .asc, same shape Central produces). + - name: Build & sign classifier fat jars (GitHub-only assets, never Central) + if: ${{ !cancelled() }} + env: + MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + run: | + set -euo pipefail + if [ -n "${MAVEN_GPG_PASSPHRASE:-}" ]; then echo "::add-mask::${MAVEN_GPG_PASSPHRASE}"; fi + # Source of truth: the set in java-llama.cpp llama/pom.xml for the pinned + # ${llama.version} (all published to Maven Central). Keep in sync on a llama bump. + CLASSIFIERS=( + cuda13-linux-x86-64 cuda13-windows-x86-64 + vulkan-linux-x86-64 vulkan-linux-aarch64 vulkan-windows-x86-64 + opencl-windows-x86-64 opencl-windows-aarch64 opencl-android-aarch64 + rocm-linux-x86-64 rocm-windows-x86-64 + sycl-fp16-linux-x86-64 sycl-fp32-linux-x86-64 sycl-windows-x86-64 + openvino-linux-x86-64 openvino-windows-x86-64 + msvc-windows + ) + build() { mvn -B --no-transfer-progress -pl srcmorph-cli -am -DskipTests -Dmaven.javadoc.skip=true -Dllama.classifier="$1" package; } + for c in "${CLASSIFIERS[@]}"; do + build "$c" + f=$(ls srcmorph-cli/target/srcmorph-cli-*-jar-with-dependencies.jar) + cp "$f" "${f%.jar}-${c}.jar" + done + # default CPU build LAST so the unsuffixed fat jar is the all-platform CPU variant + build "" + for f in srcmorph-cli/target/srcmorph-cli-*-jar-with-dependencies*.jar; do + printf '%s' "$MAVEN_GPG_PASSPHRASE" | gpg --batch --yes --pinentry-mode loopback --passphrase-fd 0 --detach-sign --armor "$f" + cp "$f" "$f".asc signed-release-assets/ + done - uses: actions/upload-artifact@v7 if: ${{ !cancelled() }} with: diff --git a/srcmorph-cli/README.md b/srcmorph-cli/README.md index 29874ca..0213369 100644 --- a/srcmorph-cli/README.md +++ b/srcmorph-cli/README.md @@ -14,7 +14,19 @@ java -jar srcmorph-cli--jar-with-dependencies.jar -jar-with-dependencies.jar`, built by `mvn package`) bundles every -dependency, including a logback binding, so it runs standalone. Both `.json`/`.js` (parsed via a +dependency, including a logback binding, so it runs standalone. + +**Download:** the pre-built fat jars are attached to each +[GitHub Release](https://github.com/bernardladenthin/srcmorph/releases) (with a `.asc` GPG signature), +**not** Maven Central. The unsuffixed `srcmorph-cli--jar-with-dependencies.jar` bundles the +multi-platform **CPU** `net.ladenthin:llama` native and runs on any OS/arch. For GPU acceleration there +is one classifier variant per llama native backend — +`srcmorph-cli--jar-with-dependencies-.jar` (e.g. `-cuda13-linux-x86-64`, +`-vulkan-windows-x86-64`) — each bundling exactly that one backend's binary, so it runs only on the +matching OS/arch with that GPU runtime present. Pick the plain CPU jar unless you specifically want a +GPU build. + +Both `.json`/`.js` (parsed via a Jackson `ObjectMapper`) and `.yaml`/`.yml` (parsed via `YAMLMapper`) are supported — pick whichever you prefer; both mappers are configured strictly (`FAIL_ON_UNKNOWN_PROPERTIES`), so a typo'd key fails the run immediately rather than being silently ignored. On startup the CLI logs the parsed configuration diff --git a/srcmorph-cli/pom.xml b/srcmorph-cli/pom.xml index 1d442fe..c4495db 100644 --- a/srcmorph-cli/pom.xml +++ b/srcmorph-cli/pom.xml @@ -488,8 +488,18 @@ SPDX-License-Identifier: Apache-2.0 IS the deliverable artifact (the thing a user actually downloads and runs with `java -jar`), not an occasional extra. Binding it in every `mvn package` (and therefore every `mvn verify` / release build) keeps it in lockstep with the plain jar rather than - depending on a profile flag being remembered at release time. It rides `mvn -P release - deploy` as the `jar-with-dependencies` classifier alongside the plain jar. + depending on a profile flag being remembered at release time. + + `false`: the fat jar is a GitHub-Release-only asset and must NOT be + deployed to Maven Central. A jar-with-dependencies on Central is redundant (consumers + depend on the plain `srcmorph-cli` jar + its resolved dependency graph, never the fat + jar) and, because it bundles the `net.ladenthin:llama` native binary, is both large and + platform-specific. Leaving it attached uploaded a 74 MB fat jar to Central on every + release. With attach=false the file is still produced in target/ (so the CI build's + upload-artifact and the release job's classifier-fat-jar loop still pick it up) but is + neither installed to ~/.m2 nor deployed to Central. Signing + GitHub attachment (for + this default CPU jar and one fat jar per net.ladenthin:llama GPU classifier) is done + explicitly by the publish-release / publish-snapshot jobs in .github/workflows/publish.yml. --> org.apache.maven.plugins @@ -503,6 +513,7 @@ SPDX-License-Identifier: Apache-2.0 net.ladenthin.srcmorph.cli.Main + false