Harden release CI
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 28/100
Research direction
Start with .github/workflows/release.yaml, the release profile in pom.xml, and .github/dependabot.yml; run the documented zizmor command and inspect the current Maven release flow. Done means the agreed in-scope release hardening is implemented, verified, and preserves the stated out-of-scope decisions and repo-admin follow-up items.
Written by the indexing model from the issue text.
Description
Why now
A static-analysis pass with zizmor flagged 37 high-severity findings across .github/workflows/, including six template-injection sites in the release workflow itself. Independent of that, the workflow has no tests gating the publish step, so a regression on main ships to Central. To fix, its largely mechanical but they're coupled enough that doing them as one design pass is cleaner than one-off PRs.
Problems in the current release pipeline
Working through .github/workflows/release.yaml and the release profile in pom.xml:
- No tests gate the release. Both
mvn -Dquicklyandmvn deploy ... -DskipTests=trueskip the test suite. The dispatch button publishes whatever is onmain. - No protected environment around the deploy.
workflow_dispatchcan be invoked by anyone withworkflowwrite access; the release secrets (GPG_PRIVATE_KEY,SONATYPE_*,GH_TOKEN) are exposed at the repo level. branch:input is dead. The workflow takes abranch:input butactions/checkoutnever references it. Dispatching from a non-default branch silently releasesmain.- Attestations would land AFTER
mvn deploysucceeds — if attestation issuance fails, jars are already on Central, unattested. Want:verify→ attest the bytes →deploy. - PAT (
GH_TOKEN) used to push tomain.mvn versions:set+git pushbypasses branch protection. The PAT is long-lived and exfiltratable. - GPG key imported twice; once via insecure shell expansion.
setup-javaalready imports the GPG key; the secondcat <(echo -e "${{ secrets.GPG_PRIVATE_KEY }}") | gpg --batch --importstep expands the secret into a rendered shell command. mvn -Xin the deploy step. Full debug output; some Maven plugins log credentials and signing-key paths at DEBUG.- No SHA pinning on any action.
actions/checkout@v6,actions/setup-java@v5, etc. across every workflow. Mutable tags = compromised-publisher / tag-mover risk. - Over-scoped
permissions:at the workflow level — unuseddeployments: writeandpackages: write. softprops/action-gh-releasecreates an empty GH release — no jars, no SBOM, no checksums attached.- Redundant
Compilestep runs before deploy and is then thrown away by the subsequentclean deploy. SONATYPE_USERNAME/SONATYPE_PASSWORD— need to confirm these are Central Portal user tokens, not legacy OSSRH credentials.- Two commits to
mainper release (Release version update XandSnapshot version update). Tag points at a commit that's no longer onmain. - No reproducible-build flag. Re-running the workflow produces a different jar SHA each time; attestations only prove "built once at this time," not "this exact bytes."
central-publishing-maven-plugin0.10.0 — currently the latest, but worth flagging for periodic bumps.- No concurrency group — two simultaneous dispatches will conflict on git pushes.
Proposed changes
1. Test-gated release (P0)
Replace the deploy invocation with a three-phase flow:
mvn verify -Drelease— compile, run full test suite, sign (GPG), generate aggregate CycloneDX SBOM. A failure here stops the release before anything is published.actions/attest-build-provenanceandactions/attest-sbomagainst the bytes produced.mvn deploy -Drelease -DskipTests=true -Dgpg.skip=true— reuses the artifacts and signatures fromverify.
2. Defer all git pushes until after Central deploy succeeds (P0)
Edit pom.xml versions in the working tree, build, attest, deploy. Only then create the version commit, tag, and push. If anything fails before deploy, the remote is untouched and the dispatch is safe to re-run.
3. maven-central GitHub Environment (P0)
Add environment: maven-central to the release job. Repo admin will need to:
- Create the environment in repo settings.
- Add required reviewers.
- Set a deployment-branch policy restricting refs.
- Move the release secrets (
GPG_PRIVATE_KEY,GPG_PASSPHRASE,SONATYPE_USERNAME,SONATYPE_PASSWORD,GH_TOKEN) onto the environment so they cannot be read by unrelated workflows.
4. SLSA build provenance and SBOM attestations
- Add
actions/attest-build-provenance@<sha> # v3against**/target/*.{jar,pom,jar.asc,pom.asc}. - Add a
cyclonedx-maven-pluginexecution in thereleaseprofile (root-only via<inherited>false</inherited>,makeAggregateBombound topackage). - Add
actions/attest-sbom@<sha> # v3against**/target/*.jarwith the aggregatetarget/bom.json.
Configure the SBOM plugin with <includeBomSerialNumber>false</includeBomSerialNumber> so it's deterministic across rebuilds at the same SHA (otherwise the SBOM hash changes between the verify we attest and the deploy re-package). Also <skipNotDeployed>false</skipNotDeployed> because central-publishing sets maven.deploy.skip=true on the aggregator pom.
5. Reproducible builds
Set <project.build.outputTimestamp>1980-02-01T00:00:00Z</project.build.outputTimestamp> in the root pom. Strips timestamps from JAR entries so re-runs at the same SHA produce byte-identical artifacts. Required for SLSA-3 verification to mean what people think it means.
Note: the maven-shade-plugin's intermediate original-*.jar files (never deployed) will not be reproducible. Released artifacts will be.
6. SHA-pin every action
Replace every major-tag reference with a commit SHA + trailing version comment. The actions currently in use, with versions resolved live from each upstream's GitHub repo at the time this issue was drafted:
| Action | Tag | Commit SHA |
|---|---|---|
actions/checkout |
v6.0.2 | de0fac2e4500dabe0009e67214ff5f5447ce83dd |
actions/setup-java |
v5.2.0 | be666c2fcd27ec809703dec50e508c2fdc7f6654 |
actions/setup-node |
v6.4.0 | 48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e |
actions/upload-artifact |
v7.0.1 | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a |
actions/upload-pages-artifact |
v5.0.0 | fc324d3547104276b827a68afc52ff2a11cc49c9 |
actions/deploy-pages |
v5.0.0 | cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 |
gradle/actions/setup-gradle |
v6.1.0 | 50e97c2cd7a37755bbfafc9c5b7cafaece252f6e |
actions/attest-build-provenance |
v3 | 977bb373ede98d70efdf65b84cb5f73e068dcc2a |
actions/attest-sbom |
v3 | 4651f806c01d8637787e274ac3bdf724ef169f34 |
Versions should be re-resolved at PR time in case there are newer point releases.
7. Dependabot grouping for github-actions
.github/dependabot.yml already has package-ecosystem: github-actions on a daily schedule. With ~9 SHA-pinned actions across the repo, each Dependabot run will fan out into one PR per action. Add a groups: block batching all action bumps into one weekly PR. Dependabot preserves the # vX.Y.Z trailing comment when bumping a SHA reference, so the audit trail stays readable.
8. zizmor findings to fix in release.yaml
Running uvx zizmor .github/workflows/release.yaml:
- 6 ×
template-injection(High) —${{ github.event.inputs.* }}expanded directly into shell commands. Route inputs throughenv:vars referenced as"$VAR"so the shell, not the YAML preprocessor, expands them. - 1 ×
superfluous-actions(Info) —softprops/action-gh-releaseis redundant whengh release createis available via the GitHub-suppliedghCLI. Drop the third-party action dependency.
In --persona=auditor mode, additional fixes:
- 3 ×
excessive-permissions— scopecontents/id-token/attestations: writeto the job level. Setpermissions: {}at workflow scope as default-deny. undocumented-permissions— add inline trailing comments justifying each permission line.
9. Sigstore keyless signing (getting into only nice-to-have territory)
Add dev.sigstore:sigstore-maven-plugin 1.3.0 to the release profile, bound to verify. Produces <artifact>.sigstore.json bundles OIDC-bound to the workflow identity. These ride with the artifact to Maven Central so consumers can verify without contacting GitHub. In the future, you could phase out maven-gpg although I don't think that's practical in the near-term.
10. Other repo-wide hygiene
- Remove the redundant Compile step.
- Remove
mvn -Xfrom production. - Remove the duplicate
gpg --batch --importstep (setup-java already handles it). persist-credentials: falseonactions/checkout; explicitgit remote set-urlto injectGH_TOKENonly at the push step.- Drop unused
deployments: writeandpackages: writepermissions. - Attach the deployed jars +
sbom-cyclonedx.json+SHA256SUMSto the GitHub release. - Add a
concurrency: { group: release, cancel-in-progress: false }block.
11. Other workflows still have zizmor findings
Not in scope for the release pipeline rebuild, but worth tracking:
- 2 High + 2 Medium
excessive-permissionsinci.yaml,perf.yaml,deploy-docs.yml(workflow-level perms that should be job-scoped). - 14 Low
artipackedwarnings — everyactions/checkoutwithoutpersist-credentials: false.zizmor --fix=safehandles these mechanically. - 7 Info
template-injectioninperf.yaml—${{ needs.<job>.outputs.result-link }}in shell. Low practical risk (input source is a JMH result URL from an upstream job) but the env-var fix is mechanical.
Out of scope (deferred)
R5 / R13 — eliminating the PAT-push-to-main pattern entirely. The proper fix is ${revision} placeholders + flatten-maven-plugin so no version-edit commits are needed, or a release-please-style PR flow. Both touch every module pom and deserve a dedicated design discussion.
Action items requiring repo-admin or human follow-up
- Create
maven-centralGitHub Environment with required reviewers + deployment-branch policy. Move the five release secrets onto it. - Confirm
SONATYPE_USERNAME/SONATYPE_PASSWORDare Central Portal user tokens (not legacy OSSRH credentials). - After first release with both GPG and Sigstore signatures, decide on Sigstore phase 2 (drop GPG, retire long-lived key).
- Decide on R5 / R13 direction (
${revision}vs release-please vs status quo).
- Dominant language
- Java
- Stars
- 306
- Forks
- 20
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 35
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from bytecodealliance/endive
-
Difficulty 4/5 3-5 days Newbie friendliness 45/100
bytecodealliance/endive#211 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
bytecodealliance/endive#203 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 25/100
bytecodealliance/endive#202 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 55/100
bytecodealliance/endive#201 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
bytecodealliance/endive#181 · 3 comments · 1 reaction ·
Maintainers usually reply within 1 day
All issues in bytecodealliance/endive
Similar issues
-
Update license yearOpen0 - Backlog 1 - Ready documentation good first issue help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
cbor
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
FasterXML/jackson-dataformats-binary#844 ·
Maintainers usually reply within 1 day
-
improvement
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
apache/iceberg#18351 · 1 comment ·
Maintainers usually reply within 1 day
-
bug good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
repowise-dev/repowise#2945 · 1 comment ·
Maintainers usually reply within 1 day
-
Interpolating settings.xml can lead to malformed XML when variable value contains double-hyphenOpenbug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
apache/maven#13321 · 1 comment ·
Maintainers usually reply within 1 day