From 1b9f3270a528ff34688fa5c8d0495383fb53b463 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 23:13:57 +0000 Subject: [PATCH 1/6] ci: add CodeQL SAST, CycloneDX SBOM, and build attestations - New CodeQL workflow runs security-and-quality queries on push, PR, and weekly schedule to catch SAST issues alongside the existing Dependabot SCA coverage. - cyclonedx-maven-plugin generates a CycloneDX 1.6 SBOM (XML + JSON) for compile/runtime/provided dependencies during the package phase, attached as a build artifact so it's published to Maven Central with the JAR. - release.yml and snapshot.yml now produce GitHub build provenance and SBOM attestations (actions/attest-build-provenance, attest-sbom) so downstream consumers can verify artifacts via gh attestation verify. Release workflow also uploads the SBOM to the GitHub Release. --- .github/workflows/codeql.yml | 47 ++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 24 +++++++++++++++++ .github/workflows/snapshot.yml | 24 +++++++++++++++++ pom.xml | 23 +++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..c792d3c2 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,47 @@ +--- +name: CodeQL + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: '0 6 * * 1' + +permissions: + contents: read + +jobs: + analyze: + name: Analyze (Java) + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + security-events: write + packages: read + actions: read + contents: read + + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-java@v5 + with: + java-version: '17' + distribution: 'temurin' + cache: 'maven' + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: java-kotlin + queries: security-and-quality + + - name: Build with Maven + run: ./mvnw -B -ntp clean compile -DskipTests + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:java-kotlin" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 839af913..8105419c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,8 @@ jobs: release: permissions: contents: write + id-token: write + attestations: write runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 @@ -46,6 +48,28 @@ jobs: OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }} MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + - name: Attest build provenance for release artifacts + uses: actions/attest-build-provenance@v2 + with: + subject-path: | + target/checkout/target/elearning-module-parser-*.jar + + - name: Attest SBOM for release artifacts + uses: actions/attest-sbom@v2 + with: + subject-path: target/checkout/target/elearning-module-parser-*.jar + sbom-path: target/checkout/target/elearning-module-parser-*-cyclonedx.json + + - name: Upload SBOM and provenance to GitHub Release + run: | + TAG=$(grep 'scm.tag=' release.properties | cut -d'=' -f2) + gh release upload "${TAG}" \ + target/checkout/target/elearning-module-parser-*-cyclonedx.json \ + target/checkout/target/elearning-module-parser-*-cyclonedx.xml \ + --clobber || echo "Release ${TAG} not found yet; skipping SBOM upload" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Update documentation with released version run: | TAG=$(grep 'scm.tag=' release.properties | cut -d'=' -f2) diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 702f4c79..138ae8a2 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -22,6 +22,8 @@ jobs: packages: write statuses: write checks: write + id-token: write + attestations: write steps: - uses: actions/checkout@v6 @@ -39,6 +41,28 @@ jobs: env: MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + - name: Attest build provenance for snapshot artifacts + if: github.event_name != 'pull_request' + uses: actions/attest-build-provenance@v2 + with: + subject-path: target/elearning-module-parser-*.jar + + - name: Attest SBOM for snapshot artifacts + if: github.event_name != 'pull_request' + uses: actions/attest-sbom@v2 + with: + subject-path: target/elearning-module-parser-*.jar + sbom-path: target/elearning-module-parser-*-cyclonedx.json + + - name: Upload SBOM as workflow artifact + uses: actions/upload-artifact@v4 + with: + name: sbom + path: | + target/elearning-module-parser-*-cyclonedx.json + target/elearning-module-parser-*-cyclonedx.xml + if-no-files-found: warn + - name: Test Summary id: test_summary uses: test-summary/action@v2 diff --git a/pom.xml b/pom.xml index 959fba93..ef506abb 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,7 @@ 20251224 0.10.0 3.15.0 + 2.9.1 3.6.3 3.5.5 3.2.8 @@ -401,6 +402,28 @@ + + org.cyclonedx + cyclonedx-maven-plugin + ${version.plugin.cyclonedx} + + + make-bom + package + + makeAggregateBom + + + + + library + 1.6 + all + ${project.artifactId}-${project.version}-cyclonedx + true + false + + org.jacoco jacoco-maven-plugin From d70e7858fa36137bcab0ff3fca6ba0cabae99b02 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 23:28:22 +0000 Subject: [PATCH 2/6] ci: fix attestation timing and ensure GitHub Release exists for SBOM upload - snapshot.yml: move attestation steps to after the GitHub Packages deploy. The deploy re-runs the package phase and overwrites target/, so attesting before deploy produced digests that did not match the published JARs, making the attestations unverifiable for consumers. - release.yml: replace `gh release upload` with `gh release create` since `mvn release:perform` only creates a git tag, not a GitHub Release. The previous command always fell through the `|| echo` guard and the SBOM was never attached to the release. --- .github/workflows/release.yml | 7 ++--- .github/workflows/snapshot.yml | 48 ++++++++++++++++++---------------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8105419c..6eb3f7cd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,13 +60,14 @@ jobs: subject-path: target/checkout/target/elearning-module-parser-*.jar sbom-path: target/checkout/target/elearning-module-parser-*-cyclonedx.json - - name: Upload SBOM and provenance to GitHub Release + - name: Upload SBOM to GitHub Release run: | TAG=$(grep 'scm.tag=' release.properties | cut -d'=' -f2) - gh release upload "${TAG}" \ + gh release create "${TAG}" \ target/checkout/target/elearning-module-parser-*-cyclonedx.json \ target/checkout/target/elearning-module-parser-*-cyclonedx.xml \ - --clobber || echo "Release ${TAG} not found yet; skipping SBOM upload" + --title "${TAG}" \ + --generate-notes env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 138ae8a2..828039af 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -41,28 +41,6 @@ jobs: env: MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} - - name: Attest build provenance for snapshot artifacts - if: github.event_name != 'pull_request' - uses: actions/attest-build-provenance@v2 - with: - subject-path: target/elearning-module-parser-*.jar - - - name: Attest SBOM for snapshot artifacts - if: github.event_name != 'pull_request' - uses: actions/attest-sbom@v2 - with: - subject-path: target/elearning-module-parser-*.jar - sbom-path: target/elearning-module-parser-*-cyclonedx.json - - - name: Upload SBOM as workflow artifact - uses: actions/upload-artifact@v4 - with: - name: sbom - path: | - target/elearning-module-parser-*-cyclonedx.json - target/elearning-module-parser-*-cyclonedx.xml - if-no-files-found: warn - - name: Test Summary id: test_summary uses: test-summary/action@v2 @@ -108,3 +86,29 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + + # Attestations must run AFTER deploy: `mvn deploy` re-runs the package + # phase and overwrites target/, so only the post-deploy files match what + # was actually published to GitHub Packages. + - name: Attest build provenance for snapshot artifacts + if: github.event_name != 'pull_request' + uses: actions/attest-build-provenance@v2 + with: + subject-path: target/elearning-module-parser-*.jar + + - name: Attest SBOM for snapshot artifacts + if: github.event_name != 'pull_request' + uses: actions/attest-sbom@v2 + with: + subject-path: target/elearning-module-parser-*.jar + sbom-path: target/elearning-module-parser-*-cyclonedx.json + + - name: Upload SBOM as workflow artifact + if: github.event_name != 'pull_request' + uses: actions/upload-artifact@v4 + with: + name: sbom + path: | + target/elearning-module-parser-*-cyclonedx.json + target/elearning-module-parser-*-cyclonedx.xml + if-no-files-found: warn From 7b200cb8bbab32c428aaa094df76e184c0e01bb3 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 23:37:31 +0000 Subject: [PATCH 3/6] ci: scope SBOM attestation to the main JAR only The library SBOM describes the main artifact's dependency graph. The previous subject-path glob also matched -sources and -javadoc JARs, which would have published an SBOM attestation claiming dependencies those artifacts do not have. Build provenance attestations still cover all three JARs since they are all genuine build outputs. --- .github/workflows/release.yml | 5 ++++- .github/workflows/snapshot.yml | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6eb3f7cd..5a5182bb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,7 +57,10 @@ jobs: - name: Attest SBOM for release artifacts uses: actions/attest-sbom@v2 with: - subject-path: target/checkout/target/elearning-module-parser-*.jar + subject-path: | + target/checkout/target/elearning-module-parser-*.jar + !target/checkout/target/elearning-module-parser-*-sources.jar + !target/checkout/target/elearning-module-parser-*-javadoc.jar sbom-path: target/checkout/target/elearning-module-parser-*-cyclonedx.json - name: Upload SBOM to GitHub Release diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 828039af..67c732a5 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -100,7 +100,10 @@ jobs: if: github.event_name != 'pull_request' uses: actions/attest-sbom@v2 with: - subject-path: target/elearning-module-parser-*.jar + subject-path: | + target/elearning-module-parser-*.jar + !target/elearning-module-parser-*-sources.jar + !target/elearning-module-parser-*-javadoc.jar sbom-path: target/elearning-module-parser-*-cyclonedx.json - name: Upload SBOM as workflow artifact From 17b8fd55e4f75bec06f217047178849c3397b5ef Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 23:45:40 +0000 Subject: [PATCH 4/6] ci: make release SBOM upload idempotent and fail on missing SBOM in snapshot - release.yml: the GitHub Release upload runs after the irreversible Maven Central publish in `mvn release:perform`. Mark the step `continue-on-error: true` so a transient gh API error does not fail the job once Central has already accepted the version, and detect whether the release already exists so a rerun uploads with --clobber instead of erroring on `gh release create`. - snapshot.yml: switch the SBOM workflow artifact upload from `if-no-files-found: warn` to `error` so a regression in the SBOM packaging path fails the build instead of silently passing. --- .github/workflows/release.yml | 23 ++++++++++++++++++----- .github/workflows/snapshot.yml | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5a5182bb..a6d86fd8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,13 +64,26 @@ jobs: sbom-path: target/checkout/target/elearning-module-parser-*-cyclonedx.json - name: Upload SBOM to GitHub Release + # Maven Central publish above is irreversible; do not fail the job if a + # transient gh API error or a rerun on an existing release prevents + # this step from completing. The step is still idempotent: on a rerun + # we upload to the existing release with --clobber, so a successful + # retry replaces any prior assets cleanly. + continue-on-error: true run: | TAG=$(grep 'scm.tag=' release.properties | cut -d'=' -f2) - gh release create "${TAG}" \ - target/checkout/target/elearning-module-parser-*-cyclonedx.json \ - target/checkout/target/elearning-module-parser-*-cyclonedx.xml \ - --title "${TAG}" \ - --generate-notes + if gh release view "${TAG}" >/dev/null 2>&1; then + gh release upload "${TAG}" \ + target/checkout/target/elearning-module-parser-*-cyclonedx.json \ + target/checkout/target/elearning-module-parser-*-cyclonedx.xml \ + --clobber + else + gh release create "${TAG}" \ + target/checkout/target/elearning-module-parser-*-cyclonedx.json \ + target/checkout/target/elearning-module-parser-*-cyclonedx.xml \ + --title "${TAG}" \ + --generate-notes + fi env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 67c732a5..1b4a9070 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -114,4 +114,4 @@ jobs: path: | target/elearning-module-parser-*-cyclonedx.json target/elearning-module-parser-*-cyclonedx.xml - if-no-files-found: warn + if-no-files-found: error From 904a6f4721ceb86d08d66e878bb72329cd4aa799 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 6 May 2026 00:12:37 +0000 Subject: [PATCH 5/6] ci: split attestation into a separate job to isolate OIDC permissions The Maven build steps execute repository-controlled plugin code, so granting id-token: write and attestations: write to the same job that runs `mvn` made those credentials reachable from any Maven plugin. Move attestations and the GitHub Release SBOM upload into a dedicated post-build job that only has those permissions; the build job stays read-only for OIDC. This also removes the need for `continue-on-error` on the GitHub Release upload: the Maven Central publish completes in the release job (which stays green once Central accepts the version), and a failure in the attest job is now a visibly red, rerunnable job instead of a silently green workflow with no follow-up signal. Artifacts are staged into a directory and uploaded with if-no-files-found: error so a regression in the SBOM packaging path fails the build. --- .github/workflows/release.yml | 116 +++++++++++++++++++++------------ .github/workflows/snapshot.yml | 58 ++++++++++++----- 2 files changed, 117 insertions(+), 57 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a6d86fd8..671ac7ce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,10 +2,7 @@ name: Release permissions: - contents: write - packages: write - statuses: write - checks: write + contents: read on: workflow_dispatch: @@ -14,9 +11,11 @@ jobs: release: permissions: contents: write - id-token: write - attestations: write + packages: write runs-on: ubuntu-latest + outputs: + tag: ${{ steps.publish.outputs.tag }} + version: ${{ steps.publish.outputs.version }} steps: - uses: actions/checkout@v6 - uses: webfactory/ssh-agent@v0.10.0 @@ -34,6 +33,7 @@ jobs: gpg-passphrase: MAVEN_GPG_PASSPHRASE - name: Publish package to Maven Central + id: publish run: | git config --global user.name "github-actions[bot]" git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" @@ -41,6 +41,8 @@ jobs: cat release.properties TAG=$(grep 'scm.tag=' release.properties | cut -d'=' -f2) VERSION=${TAG#v} + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" mvn -B -ntp -Dstyle.color=always release:perform -DconnectionUrl=scm:git:https://github.com/${{ github.repository }}.git echo "Released ${TAG} 🚀" >> $GITHUB_STEP_SUMMARY env: @@ -48,44 +50,20 @@ jobs: OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }} MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} - - name: Attest build provenance for release artifacts - uses: actions/attest-build-provenance@v2 - with: - subject-path: | - target/checkout/target/elearning-module-parser-*.jar + - name: Stage release artifacts for attestation + run: | + mkdir -p release-artifacts + cp target/checkout/target/elearning-module-parser-*.jar release-artifacts/ + cp target/checkout/target/elearning-module-parser-*-cyclonedx.json release-artifacts/ + cp target/checkout/target/elearning-module-parser-*-cyclonedx.xml release-artifacts/ - - name: Attest SBOM for release artifacts - uses: actions/attest-sbom@v2 + - name: Upload release artifacts for attestation job + uses: actions/upload-artifact@v4 with: - subject-path: | - target/checkout/target/elearning-module-parser-*.jar - !target/checkout/target/elearning-module-parser-*-sources.jar - !target/checkout/target/elearning-module-parser-*-javadoc.jar - sbom-path: target/checkout/target/elearning-module-parser-*-cyclonedx.json - - - name: Upload SBOM to GitHub Release - # Maven Central publish above is irreversible; do not fail the job if a - # transient gh API error or a rerun on an existing release prevents - # this step from completing. The step is still idempotent: on a rerun - # we upload to the existing release with --clobber, so a successful - # retry replaces any prior assets cleanly. - continue-on-error: true - run: | - TAG=$(grep 'scm.tag=' release.properties | cut -d'=' -f2) - if gh release view "${TAG}" >/dev/null 2>&1; then - gh release upload "${TAG}" \ - target/checkout/target/elearning-module-parser-*-cyclonedx.json \ - target/checkout/target/elearning-module-parser-*-cyclonedx.xml \ - --clobber - else - gh release create "${TAG}" \ - target/checkout/target/elearning-module-parser-*-cyclonedx.json \ - target/checkout/target/elearning-module-parser-*-cyclonedx.xml \ - --title "${TAG}" \ - --generate-notes - fi - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + name: release-artifacts + path: release-artifacts/ + if-no-files-found: error + retention-days: 7 - name: Update documentation with released version run: | @@ -106,3 +84,57 @@ jobs: git push env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Attestations and the GitHub Release SBOM upload run in a separate job so + # the OIDC token (id-token: write) and attestations: write permission are + # never available to the preceding `mvn release:*` steps, which execute + # repository-controlled plugin code. If this job fails after Maven Central + # has accepted the version, the release job stays green (the publish did + # succeed), this job is visibly red, and "Re-run failed jobs" reruns only + # the attestation/Release steps without retrying the Central publish. + attest: + needs: release + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + attestations: write + steps: + - name: Download release artifacts + uses: actions/download-artifact@v4 + with: + name: release-artifacts + path: artifacts + + - name: Attest build provenance for release artifacts + uses: actions/attest-build-provenance@v2 + with: + subject-path: | + artifacts/elearning-module-parser-*.jar + + - name: Attest SBOM for release artifacts + uses: actions/attest-sbom@v2 + with: + subject-path: | + artifacts/elearning-module-parser-*.jar + !artifacts/elearning-module-parser-*-sources.jar + !artifacts/elearning-module-parser-*-javadoc.jar + sbom-path: artifacts/elearning-module-parser-*-cyclonedx.json + + - name: Upload SBOM to GitHub Release + run: | + TAG="${{ needs.release.outputs.tag }}" + if gh release view "${TAG}" >/dev/null 2>&1; then + gh release upload "${TAG}" \ + artifacts/elearning-module-parser-*-cyclonedx.json \ + artifacts/elearning-module-parser-*-cyclonedx.xml \ + --clobber + else + gh release create "${TAG}" \ + artifacts/elearning-module-parser-*-cyclonedx.json \ + artifacts/elearning-module-parser-*-cyclonedx.xml \ + --title "${TAG}" \ + --generate-notes + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 1b4a9070..6de269fb 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -22,8 +22,6 @@ jobs: packages: write statuses: write checks: write - id-token: write - attestations: write steps: - uses: actions/checkout@v6 @@ -87,31 +85,61 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} - # Attestations must run AFTER deploy: `mvn deploy` re-runs the package - # phase and overwrites target/, so only the post-deploy files match what - # was actually published to GitHub Packages. - - name: Attest build provenance for snapshot artifacts + # Stage post-deploy artifacts (these are exactly what GitHub Packages + # received, since `mvn deploy` re-runs the package phase and overwrites + # target/) for the separate attest job to consume. + - name: Stage snapshot artifacts for attestation + if: github.event_name != 'pull_request' + run: | + mkdir -p snapshot-artifacts + cp target/elearning-module-parser-*.jar snapshot-artifacts/ + cp target/elearning-module-parser-*-cyclonedx.json snapshot-artifacts/ + cp target/elearning-module-parser-*-cyclonedx.xml snapshot-artifacts/ + + - name: Upload snapshot artifacts for attestation job if: github.event_name != 'pull_request' + uses: actions/upload-artifact@v4 + with: + name: snapshot-artifacts + path: snapshot-artifacts/ + if-no-files-found: error + + # Attestations run in a separate job so id-token: write and + # attestations: write are not available to the preceding Maven build, + # which executes repository-controlled plugin code. + attest: + needs: test-build + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + permissions: + id-token: write + attestations: write + steps: + - name: Download snapshot artifacts + uses: actions/download-artifact@v4 + with: + name: snapshot-artifacts + path: artifacts + + - name: Attest build provenance for snapshot artifacts uses: actions/attest-build-provenance@v2 with: - subject-path: target/elearning-module-parser-*.jar + subject-path: artifacts/elearning-module-parser-*.jar - name: Attest SBOM for snapshot artifacts - if: github.event_name != 'pull_request' uses: actions/attest-sbom@v2 with: subject-path: | - target/elearning-module-parser-*.jar - !target/elearning-module-parser-*-sources.jar - !target/elearning-module-parser-*-javadoc.jar - sbom-path: target/elearning-module-parser-*-cyclonedx.json + artifacts/elearning-module-parser-*.jar + !artifacts/elearning-module-parser-*-sources.jar + !artifacts/elearning-module-parser-*-javadoc.jar + sbom-path: artifacts/elearning-module-parser-*-cyclonedx.json - name: Upload SBOM as workflow artifact - if: github.event_name != 'pull_request' uses: actions/upload-artifact@v4 with: name: sbom path: | - target/elearning-module-parser-*-cyclonedx.json - target/elearning-module-parser-*-cyclonedx.xml + artifacts/elearning-module-parser-*-cyclonedx.json + artifacts/elearning-module-parser-*-cyclonedx.xml if-no-files-found: error From 33c1821e81ad54431b7eb0a33ca7919e1ed9f331 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 13:52:23 +0000 Subject: [PATCH 6/6] ci: fix release attestation provenance and remove staging from release job Agent-Logs-Url: https://github.com/jcputney/elearning-module-parser/sessions/6afe0834-5f84-4b8e-872d-75122168c551 Co-authored-by: jcputney <42720634+jcputney@users.noreply.github.com> --- .github/workflows/release.yml | 55 +++++++++++++++++------------------ 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 671ac7ce..0b697a97 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,21 +50,6 @@ jobs: OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }} MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} - - name: Stage release artifacts for attestation - run: | - mkdir -p release-artifacts - cp target/checkout/target/elearning-module-parser-*.jar release-artifacts/ - cp target/checkout/target/elearning-module-parser-*-cyclonedx.json release-artifacts/ - cp target/checkout/target/elearning-module-parser-*-cyclonedx.xml release-artifacts/ - - - name: Upload release artifacts for attestation job - uses: actions/upload-artifact@v4 - with: - name: release-artifacts - path: release-artifacts/ - if-no-files-found: error - retention-days: 7 - - name: Update documentation with released version run: | TAG=$(grep 'scm.tag=' release.properties | cut -d'=' -f2) @@ -92,6 +77,9 @@ jobs: # has accepted the version, the release job stays green (the publish did # succeed), this job is visibly red, and "Re-run failed jobs" reruns only # the attestation/Release steps without retrying the Central publish. + # The job checks out the release tag so that provenance is anchored to the + # tagged release commit (not the pre-release snapshot commit that triggered + # the workflow_dispatch), and rebuilds the artifacts cleanly from that commit. attest: needs: release runs-on: ubuntu-latest @@ -100,39 +88,50 @@ jobs: id-token: write attestations: write steps: - - name: Download release artifacts - uses: actions/download-artifact@v4 + - name: Checkout release tag + uses: actions/checkout@v6 + with: + ref: ${{ needs.release.outputs.tag }} + + - name: Set up Java + uses: actions/setup-java@v5 with: - name: release-artifacts - path: artifacts + java-version: "17" + distribution: "temurin" + cache: "maven" + + - name: Build release artifacts at tagged commit + run: mvn -B -ntp package -DskipTests -Dgpg.skip=true - name: Attest build provenance for release artifacts uses: actions/attest-build-provenance@v2 with: subject-path: | - artifacts/elearning-module-parser-*.jar + target/elearning-module-parser-*.jar + !target/elearning-module-parser-*-sources.jar + !target/elearning-module-parser-*-javadoc.jar - name: Attest SBOM for release artifacts uses: actions/attest-sbom@v2 with: subject-path: | - artifacts/elearning-module-parser-*.jar - !artifacts/elearning-module-parser-*-sources.jar - !artifacts/elearning-module-parser-*-javadoc.jar - sbom-path: artifacts/elearning-module-parser-*-cyclonedx.json + target/elearning-module-parser-*.jar + !target/elearning-module-parser-*-sources.jar + !target/elearning-module-parser-*-javadoc.jar + sbom-path: target/elearning-module-parser-*-cyclonedx.json - name: Upload SBOM to GitHub Release run: | TAG="${{ needs.release.outputs.tag }}" if gh release view "${TAG}" >/dev/null 2>&1; then gh release upload "${TAG}" \ - artifacts/elearning-module-parser-*-cyclonedx.json \ - artifacts/elearning-module-parser-*-cyclonedx.xml \ + target/elearning-module-parser-*-cyclonedx.json \ + target/elearning-module-parser-*-cyclonedx.xml \ --clobber else gh release create "${TAG}" \ - artifacts/elearning-module-parser-*-cyclonedx.json \ - artifacts/elearning-module-parser-*-cyclonedx.xml \ + target/elearning-module-parser-*-cyclonedx.json \ + target/elearning-module-parser-*-cyclonedx.xml \ --title "${TAG}" \ --generate-notes fi