diff --git a/.github/shared-files.sha256 b/.github/shared-files.sha256 index e047d4e3..6600a55b 100644 --- a/.github/shared-files.sha256 +++ b/.github/shared-files.sha256 @@ -67,5 +67,5 @@ fcb6cd0e2a71205b918f545f2680d378c790b774b9852129c5f9c7f3f8eb9860 .github/workfl 09230b04c5f14bdda50a0c862152ff197a656cbc3fd0d110899c5f8150f8aa9a .github/workflows/publish.yml#check-snapshot 51e987d59efd9887b43e345edc0e0f3db21686879e3ca6ea9f0fdbc493d37121 .github/workflows/publish.yml#check-tag a8637f88765da759a6eaba243d0d0d695053174df581ccbbcc5e5f3a475fc9e9 .github/workflows/publish.yml#verify-signing-key-gradle -de7ae553e6ac327094bcedf3acacd8b0c9e25607f9556c99419abf1dde405493 .github/workflows/publish.yml#github-snapshot -4232da9e0a0742e21b78c971642d99805b7365a025771f48fa26118849500ee9 .github/workflows/publish.yml#github-release +f20f0a991ce68fa1dcc8ef4daffc5e5dea2093d9b578aa41d17f18a56555d79f .github/workflows/publish.yml#github-snapshot +ce90d90fcb1f2de35075b91c74411e9eb7d3b43e7965fd592744e914fd04c5db .github/workflows/publish.yml#github-release diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 569f53b4..8779f138 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -635,7 +635,7 @@ jobs: MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} # Runs even when the deploy step failed: a Central publish-poll timeout reds the # job *after* the bundle was uploaded (and typically published server-side), while - # the signed jars + .asc files already exist in target/ (signing happens at + # the signed jars and poms + .asc files already exist in target/ (signing happens at # verify). Collecting on failure lets the github-snapshot job still attach them. - name: Collect signed artifacts if: ${{ !cancelled() }} @@ -643,6 +643,9 @@ jobs: mkdir -p signed-snapshot-assets cp target/*.jar signed-snapshot-assets/ 2>/dev/null || true cp target/*.jar.asc signed-snapshot-assets/ 2>/dev/null || true + # maven-gpg writes target/-.pom next to its .pom.asc; Central has both. + cp target/*.pom signed-snapshot-assets/ 2>/dev/null || true + cp target/*.pom.asc signed-snapshot-assets/ 2>/dev/null || true - uses: actions/upload-artifact@v7 if: ${{ !cancelled() }} with: @@ -676,21 +679,23 @@ jobs: run: | set -uo pipefail dir="snapshot-assets" - jars=$(find "$dir" -maxdepth 1 -name '*.jar' | sort) - if [ -z "$jars" ]; then - echo "::error::no jars in $dir -- the collection step produced nothing" + # Every attachable artifact type, not only jars: with '*.jar' alone the AARs were attached + # unsigned for months and nothing counted them, so the job reported success. + artifacts=$(find "$dir" -maxdepth 1 \( -name '*.jar' -o -name '*.aar' -o -name '*.pom' \) | sort) + if [ -z "$artifacts" ]; then + echo "::error::no artifacts in $dir -- the collection step produced nothing" echo "missing=-1" >> "$GITHUB_OUTPUT" exit 0 fi missing=0 - for jar in $jars; do - if [ ! -e "$jar.asc" ]; then - echo "::error::unsigned: $(basename "$jar") has no detached .asc" + for artifact in $artifacts; do + if [ ! -e "$artifact.asc" ]; then + echo "::error::unsigned: $(basename "$artifact") has no detached .asc" missing=$((missing + 1)) fi done echo "missing=$missing" >> "$GITHUB_OUTPUT" - [ "$missing" -eq 0 ] && echo "all $(echo "$jars" | wc -l) jar(s) signed" + [ "$missing" -eq 0 ] && echo "all $(echo "$artifacts" | wc -l) artifact(s) signed" exit 0 - name: Update snapshot pre-release env: @@ -705,6 +710,35 @@ jobs: gh release upload snapshot snapshot-assets/* \ --repo ${{ github.repository }} \ --clobber + - name: Remove the assets this run did not upload + # `snapshot` is a rolling "latest" pre-release, and `gh release upload --clobber` replaces + # same-named assets but never removes any other, so every asset of every past version stayed + # forever: 208 on java-llama.cpp (109 of them from 5.0.x, 43 in shapes that no longer exist), + # and SpotBugs plugin jars an old collect step attached in 2026-05 on three repositories. A + # non-current asset on this release is noise by definition. The keep-set is what this run + # uploaded -- the files in the asset directory -- never a version pattern, which would have to + # be re-taught at every classifier added or dropped (how the stale shapes arose). AFTER the + # upload on purpose: a failed upload must not leave the release emptier than it was. And only + # here: a versioned v* release is history, github-release never deletes anything. Every + # deletion is printed by name, so the job log is the audit trail. + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + keep="$(ls -1 snapshot-assets | sort)" + current="$(gh release view snapshot --repo ${{ github.repository }} --json assets --jq '.assets[].name' | sort)" + stale="$(comm -13 <(printf '%s\n' "$keep") <(printf '%s\n' "$current"))" + if [ -z "$stale" ]; then + echo "nothing to remove: the release holds exactly the $(printf '%s\n' "$keep" | wc -l) asset(s) of this upload" + exit 0 + fi + echo "removing $(printf '%s\n' "$stale" | wc -l) asset(s) that are not part of this upload:" + while IFS= read -r name; do + [ -n "$name" ] || continue + echo " deleting $name" + gh release delete-asset snapshot "$name" --repo ${{ github.repository }} --yes + done <<< "$stale" + echo "kept the $(printf '%s\n' "$keep" | wc -l) asset(s) of this upload" - name: Fail if anything was attached unsigned # After the upload on purpose: the assets must exist even when the signature does not. if: ${{ always() && steps.signatures.outputs.missing != '0' }} @@ -772,7 +806,7 @@ jobs: MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} # Runs even when the deploy step failed: a Central publish-poll timeout reds the # job *after* the bundle was uploaded (and typically published server-side), while - # the signed jars + .asc files already exist in target/ (signing happens at + # the signed jars and poms + .asc files already exist in target/ (signing happens at # verify). Collecting on failure lets the github-release job still attach them. - name: Collect signed artifacts if: ${{ !cancelled() }} @@ -780,6 +814,9 @@ jobs: mkdir -p signed-release-assets cp target/*.jar signed-release-assets/ 2>/dev/null || true cp target/*.jar.asc signed-release-assets/ 2>/dev/null || true + # maven-gpg writes target/-.pom next to its .pom.asc; Central has both. + cp target/*.pom signed-release-assets/ 2>/dev/null || true + cp target/*.pom.asc signed-release-assets/ 2>/dev/null || true - uses: actions/upload-artifact@v7 if: ${{ !cancelled() }} with: @@ -813,21 +850,23 @@ jobs: run: | set -uo pipefail dir="release-assets" - jars=$(find "$dir" -maxdepth 1 -name '*.jar' | sort) - if [ -z "$jars" ]; then - echo "::error::no jars in $dir -- the collection step produced nothing" + # Every attachable artifact type, not only jars: with '*.jar' alone the AARs were attached + # unsigned for months and nothing counted them, so the job reported success. + artifacts=$(find "$dir" -maxdepth 1 \( -name '*.jar' -o -name '*.aar' -o -name '*.pom' \) | sort) + if [ -z "$artifacts" ]; then + echo "::error::no artifacts in $dir -- the collection step produced nothing" echo "missing=-1" >> "$GITHUB_OUTPUT" exit 0 fi missing=0 - for jar in $jars; do - if [ ! -e "$jar.asc" ]; then - echo "::error::unsigned: $(basename "$jar") has no detached .asc" + for artifact in $artifacts; do + if [ ! -e "$artifact.asc" ]; then + echo "::error::unsigned: $(basename "$artifact") has no detached .asc" missing=$((missing + 1)) fi done echo "missing=$missing" >> "$GITHUB_OUTPUT" - [ "$missing" -eq 0 ] && echo "all $(echo "$jars" | wc -l) jar(s) signed" + [ "$missing" -eq 0 ] && echo "all $(echo "$artifacts" | wc -l) artifact(s) signed" exit 0 - name: Upload release assets uses: softprops/action-gh-release@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index eb8fa924..b69ab8aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `shared-files` job) warns where a dependency or plugin -- incl. annotation-processor paths and the Spotless formatter version -- is used in another version than in a sibling's default branch. +### Fixed +- **CI: the GitHub `snapshot` pre-release holds exactly the current build, and the release assets + include the pom.** `gh release upload --clobber` replaces same-named assets and never removes any + other, so the `snapshot` release accumulated: the six 1.8.0-SNAPSHOT jars next to the 1.9.0-SNAPSHOT ones, 6 + of its 14 assets. `github-snapshot` now deletes every asset + the run did not upload, after the upload and each printed by name; versioned releases are never + pruned, and the next snapshot run clears the backlog by itself. The collect steps copy + `-.pom` + `.pom.asc` as well (Central had them, the release did not), and + the signature check counts every attachable type (`*.jar`, `*.aar`, `*.pom`) instead of jars + alone. Shared job text with the two sibling repositories (`shared-files.sha256`), the convention + in `workspace/policies/fat-jar-release-assets.md`. + ## [1.8.0] - 2026-08-29 ### Added diff --git a/CLAUDE.md b/CLAUDE.md index c2c01e5b..313a14de 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -742,6 +742,14 @@ split), attached with a detached GPG `.asc`. The publish jobs build + sign it of second `mvn -P release,assembly verify` (which stops before `deploy`, so `central-publishing` never uploads it). The cross-repo convention + per-repo shapes are documented in [`../workspace/policies/fat-jar-release-assets.md`](../workspace/policies/fat-jar-release-assets.md). +The attach jobs are shared text with the two sibling Maven-only repositories +(`publish.yml#github-snapshot` / `#github-release` in `shared-files.sha256`): the collect steps copy +`-.pom` + `.pom.asc` with the jars (maven-gpg writes both into `target/`), the +signature check counts every attachable type (`*.jar`, `*.aar`, `*.pom`), and `github-snapshot` ends +by deleting every asset of the `snapshot` pre-release that the run did not upload, each printed by +name -- `gh release upload --clobber` never removed anything, so the release accumulated every past +version. Versioned releases are never pruned. See the policy's "Attach first, then go red" and "The +`snapshot` pre-release holds exactly the current upload". **BAF-specific smoke.** The cross-repo rule "no release asset is attached that CI has not run" is implemented here by the `smoke-fatjar` job (`needs: [build]`, gates both publish jobs): it downloads