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..0b697a97 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,7 +11,11 @@ jobs: release: permissions: contents: 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 @@ -32,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" @@ -39,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: @@ -65,3 +69,71 @@ 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. + # 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 + permissions: + contents: write + id-token: write + attestations: write + steps: + - name: Checkout release tag + uses: actions/checkout@v6 + with: + ref: ${{ needs.release.outputs.tag }} + + - name: Set up Java + uses: actions/setup-java@v5 + with: + 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: | + 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: | + 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}" \ + target/elearning-module-parser-*-cyclonedx.json \ + target/elearning-module-parser-*-cyclonedx.xml \ + --clobber + else + gh release create "${TAG}" \ + target/elearning-module-parser-*-cyclonedx.json \ + 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 702f4c79..6de269fb 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -84,3 +84,62 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + + # 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: artifacts/elearning-module-parser-*.jar + + - name: Attest SBOM for snapshot 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 as workflow artifact + uses: actions/upload-artifact@v4 + with: + name: sbom + path: | + artifacts/elearning-module-parser-*-cyclonedx.json + artifacts/elearning-module-parser-*-cyclonedx.xml + if-no-files-found: error 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