Add reproducible-build CI workflows (build + release PR)#3573
Add reproducible-build CI workflows (build + release PR)#3573italo-sampaio wants to merge 4 commits into
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.OpenSSF Scorecard
Scanned Files
|
There was a problem hiding this comment.
Pull request overview
Adds a new GitHub Actions workflow and supporting templates to run a hermetic, Docker-based reproducible build for published rskj tags, producing sha256 hashes and uploading the built artifacts for downstream verification (e.g., for rsksmart/reproducible-builds).
Changes:
- Introduces a
reproducible-buildworkflow that runs on manual dispatch (required tag input) and on push of GA-style release tags. - Adds Dockerfile and README templates that are rendered by the workflow and included in the job summary alongside computed artifact hashes.
- Builds the tagged source inside a pinned Docker base image, extracts jars/pom/module, computes sha256 hashes, and uploads them as a workflow artifact.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
.github/workflows/reproducible-build.yml |
New workflow to render templates, build a hermetic Docker image for a tag, compute hashes, and upload artifacts. |
.github/reproducible-build/Dockerfile.tmpl |
Hermetic Docker build template that fetches the tag, verifies inputs via signed checksum material, and builds/publishes artifacts into the image. |
.github/reproducible-build/README.md.tmpl |
README template documenting how to build and verify hashes locally using the generated Dockerfile/image. |
Address two review findings on PR #3573 (RSKCORE-5579): - Add a fail-fast "Validate tag" step. The tag flows into a git refspec and into sed `s|__TAG__|...|` substitutions, where a `|` in the value would break the delimiter; reject anything outside ^[a-zA-Z0-9._/-]+$ with a clear error, matching the gate style in fuzz-test.yml. - Import the RSK signing key via `curl -sSL ... | gpg --import` instead of `gpg --keyserver <https .asc URL> --recv-keys`, which misuses --keyserver (it expects an HKP keyserver, not a static .asc file). Matches the import approach already used in build_and_test.yml.
a4858f6 to
2ed66fe
Compare
fmacleal
left a comment
There was a problem hiding this comment.
Very nicee!
Excited to see it creating automatically a PR in the reproducible build repo. With the ground work done here, it will be easy to do it. :)
I have left some small comments suggestions.
2ed66fe to
e1fe177
Compare
e1fe177 to
4d5ad20
Compare
4d5ad20 to
7f2a763
Compare
| case "$REF" in | ||
| ""|*..*) echo "::error::invalid ref (empty or contains '..'): '$REF'"; exit 1;; | ||
| *[!A-Za-z0-9._/-]*) echo "::error::ref has disallowed characters: '$REF'"; exit 1;; | ||
| esac |
| set -euo pipefail | ||
| # Same command a verifier runs locally, so the artifact's hashes.txt and | ||
| # any downstream README match byte for byte. | ||
| hashes=$(docker run --rm rskj-repro:build sh -c 'sha256sum * | grep -v javadoc.jar') |
| Run the following command to verify the sha256sum of the built artifacts matches the expected values: | ||
|
|
||
| ``` | ||
| $ docker run --rm rskj/__VERSION__-__MODIFIER_LC__ sh -c 'sha256sum * | grep -v javadoc.jar' |
Add a GitHub Actions workflow that hermetically builds an rskj git ref and records the sha256 hashes of the produced jars. It runs on push to master and *-rc branches, on manual dispatch with a ref input, and as a reusable workflow_call building block so a later, release-tag-triggered workflow can reuse it to open the reproducible-builds PR. The build runs inside a pinned, hermetic Dockerfile that fetches the ref itself from canonical upstream and verifies the in-tree signed checksums before building; the runner checkout only supplies the rendered Dockerfile template and version.properties. The produced jars and a hashes.txt are uploaded as a run artifact, and the hashes, version and modifier are exposed as workflow outputs for the calling workflow. A README template for the reproducible-builds entry is included for that future workflow; this workflow renders only the Dockerfile. Trust model: this workflow is the first builder only. The hashes it emits are informational, not an authoritative attestation, and it opens no PR and publishes nothing (permissions: contents: read, no secrets). A release must still be independently rebuilt to confirm the same hashes. On a non-tag push there is moreover no published release to compare against: the run is a hermetic build and canonical-hash record, not a reproducibility-vs-release check. All privileged, cross-repo work — minting a token and opening the PR against the reproducible-builds repo — is intentionally kept out of this workflow and will live in a separate workflow that holds the only secrets and write scope. The ref is validated against empty, "..", and a git-ref character allowlist before it flows into a git refspec and into sed substitution delimiters. The signing key is imported via `curl | gpg --import` rather than `gpg --keyserver`, which expects an HKP keyserver rather than a static .asc file.
Adds reproducible-build-pr.yml: on a GA release tag, builds the canonical jars via the build-only reproducible-build.yml (workflow_call, no secrets), then mints a scoped GitHub App token and opens a PR against rsksmart/reproducible-builds with the rskj/<version>-<modifier> dir. Secrets and cross-repo write scope live solely in the open-pr job; the build half stays contents:read/no-secrets.
Key the concurrency group by the ref actually being built (inputs.ref || github.sha) instead of github.ref, so workflow_dispatch runs building different refs from the same branch no longer share a group and cancel each other. Switch to cancel-in-progress: false so an in-flight build is never aborted — including a release-tag build driven via workflow_call — and to stay consistent with reproducible-build-pr.yml.
f1cc5e5 to
b14391b
Compare
fmacleal
left a comment
There was a problem hiding this comment.
Cool!
thanks for having addressed the comments!
The security team provisioned access to the reproducible-builds repo by reusing an existing GitHub App, exposed to this workflow under the secret names RSK_CORE_GH_APP_ID / RSK_CORE_GH_APP_PRIVATE_KEY. Point the open-pr App-token step at those names.
|



Description
Automates the reproducible-build artifacts that today are produced entirely by hand for
rsksmart/reproducible-builds. Adds two GitHub Actions workflows plus the templates they render, split by privilege:.github/workflows/reproducible-build.yml— build-only, no secrets,contents: read..github/workflows/reproducible-build-pr.yml— release PR, the only holder of secrets + cross-repo write scope..github/reproducible-build/Dockerfile.tmpl— the pinned, hermetic build..github/reproducible-build/README.md.tmpl— the published release-directory README.reproducible-build.yml— build-onlyGiven a git ref (branch / tag / SHA), it builds the rskj artifacts inside the pinned Dockerfile, computes the
sha256hashes of the produced jars, writes ahashes.txt, and uploads jars + hashes as a run artifact (rskj-repro-artifacts).Triggered three ways:
pushtomasterand*-rcbranches — builds that commit and records its canonical hashes.workflow_dispatch— manual, optionalrefinput (defaults to the checked-out commit).workflow_call— reusable building block: exposeshashes,version,modifierandartifact_nameas outputs, consumed by the release-PR workflow below.It holds no secrets and only
contents: read; it opens no PR and publishes nothing.reproducible-build-pr.yml— release PRTriggered on a pushed GA release tag; it builds the canonical artifacts via the build-only workflow and opens the corresponding PR against
rsksmart/reproducible-builds. Three jobs:guard— classifies the pushed tag as GA or not (all-caps name + three-part version regex, plus a belt-and-suspenders pre-release deny-list for*-PREVIEW/*-TESTNET-*/*-rc/*-RC/*SNAPSHOT*/*-alpha/*-beta). Non-GA tags skip green, not red.push.tagsglobs can't exclude pre-release tags, so this guard does.build—uses: ./.github/workflows/reproducible-build.ymlwithref: github.ref_name. Runs only for GA tags.open-pr— the only privileged job. Downloads the build artifact (hashes.txt), renders the Dockerfile + README for therskj/<version>-<modifier>/directory (the modifier→lowercase derivation lives here; hashes come straight from the artifact so README == artifact == a verifier's local check), mints a scoped GitHub App token (create-github-app-tokenv3.2.0, pinned SHA, scoped toowner: rsksmart+repositories: reproducible-builds), clones the target overx-access-token, and opens the PR via theghCLI. Fails loudly if the release directory already exists (never overwrites a published release), uses a per-tag branch with-Breset, and agh pr listidempotency check so a re-run never opens a duplicate PR.Privilege isolation: top-level
permissions: contents: read;GITHUB_TOKENis never elevated. All secrets and all cross-repo writes are confined toopen-pr, via the App token only. The PR is opened only, merging and approving are still manual processes.Motivation and Context
Reproducible-build PRs against
rsksmart/reproducible-builds(e.g. [#139](rsksmart/reproducible-builds#139) for VETIVER-9.0.3) are currently produced entirely by hand: build the pinned Dockerfile locally, runsha256sum, hand-template therskj/<version>-<modifier>/directory, and open the PR. This is purely mechanical, so it is automated end-to-end here: a hermetic, on-demand / on-push CI build, and a release-tag-triggered workflow that renders the release directory and opens the PR — while preserving the trust model (independent rebuild still gates merge).How Has This Been Tested?
Validated in forks from both rskj and reproducible-builds:
Types of changes
Checklist: