ci: 3-stage release workflow (prepare / review / publish)#237
ci: 3-stage release workflow (prepare / review / publish)#237hedgar2017 merged 12 commits intomainfrom
Conversation
aac4106 to
fb69bed
Compare
There was a problem hiding this comment.
Pull request overview
This PR restructures the release workflow into a 3-stage pipeline (build → prepare/review → publish) with enhanced security, validation, and dry-run capabilities for pull requests. The changes introduce per-job least-privilege permissions, SHA256 checksum generation and verification, artifact bundling between stages, and a review gate before publishing. It also enables PR dry-runs with the ci:release label to test the full release pipeline without creating actual releases.
Changes:
- Restructured workflow into 3 stages: build (multi-platform matrix), prepare (bundle artifacts + checksums), review (validate bundle), and publish (create release)
- Added
permissions: {}baseline with per-job least-privilege permissions - Enabled pull request dry-runs with
ci:releaselabel requirement - Updated
publish-unit-test-result-actionto v2.23.0 (pinned commit)
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 11 comments.
| File | Description |
|---|---|
| .github/workflows/release.yaml | Complete workflow restructure with 3-stage pipeline, label-gated PR support, SHA256 checksums, bundle validation, and environment-based gates |
| .github/actions/rust-unit-tests/action.yml | Updated publish-unit-test-result-action pins to v2.23.0 for all platforms (Linux, macOS, Windows) |
Comments suppressed due to low confidence (1)
.github/workflows/release.yaml:96
- The conditional logic on lines 90-96 attempts to access
github.event.inputs.*values even when the event is not workflow_dispatch. For pull_request events, these inputs will be undefined/null, which could cause the bash test expressions[ ${{ github.event.inputs.release_windows_amd64 }} != true ]to fail with syntax errors due to empty expansion.
Consider restructuring to only access inputs when the event is workflow_dispatch, or use github.event.inputs.release_windows_amd64 || true to provide a default value.
if [ '${{ github.event_name }}' = 'workflow_dispatch' ] && [ ${GITHUB_REF_TYPE} != tag ]; then
[ ${{ github.event.inputs.release_windows_amd64 }} != true ] && WINDOWS=
[ ${{ github.event.inputs.release_macos_amd64 }} != true ] && MACOS_AMD64=
[ ${{ github.event.inputs.release_macos_arm64 }} != true ] && MACOS_ARM64=
[ ${{ github.event.inputs.release_linux_amd64_gnu }} != true ] && LINUX_AMD64_GNU=
[ ${{ github.event.inputs.release_linux_arm64_gnu }} != true ] && LINUX_ARM64_GNU=
fi
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Remove dead code: unused `id: bundle` and `tarball_name` step output
- Add explicit `if` on publish job to tolerate get-previous-release failure
- Fix double ${{ }} evaluation in review job condition
- Increase artifact retention from 2 to 5 days for manual approval window
- Add `opened` to PR event types for pre-labeled PRs
- Enforce exact binary count (4) for tag releases to catch partial builds
cc1ee3d to
60c83f6
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Manual release here: https://github.com/NomicFoundation/solx/releases/tag/60c83f6 |
Split the monolithic prepare-release job into three environment-gated stages so elevated permissions are only granted to the final publish job, which requires manual approval. This v2 workflow runs in dry-run mode for validation before replacing the original release.yaml.
…release-v2 - Guard platform-toggle block with event_name check so inputs are only read on workflow_dispatch, preventing an empty build matrix on pull_request events. - Replace find|while subshell loop with sha256sum --check so a failed checksum actually fails the step.
PR ref names (e.g. `237/merge`) contain a slash which breaks the binary mv in build-rust. Use `pr<number>` instead for pull_request events.
Only use github.ref_name when ref_type is 'tag'; otherwise fall back to inputs.prerelease_suffix or 'notag'. This avoids slashes in paths from any non-tag ref (PRs, branches) without event-specific guards.
The review job was skipped on PR builds because get-previous-release (a transitive dependency via prepare) was skipped. GitHub Actions propagates skipped status through the needs chain unless overridden. Add !cancelled() + result check so review runs whenever prepare succeeds.
Replace the temporary unconditional pull_request trigger with a proper label-gated mechanism matching the pattern used by integration-tests and coverage workflows: - Add label-check job gating on `ci:release` label (blocks forks) - Run get-previous-release and changelog builder for PRs so dry-run output is realistic - Handle pull_request event in release name identification to avoid stripping `refs/pull/*/merge` as a tag - Skip the `release` environment approval gate for PR dry-runs - Remove obsolete TODO comment about temporary trigger
The previous pin (v2, SHA 27d65e1) internally used actions/cache/restore@v4 and actions/cache/save@v4 (unpinned tags), which violates the org's SHA-pinning policy. v2.23.0 pins its cache dependencies to full SHAs.
Delete release-v2.yaml — its 3-stage architecture (prepare/review/publish), per-job permissions, SHA256 checksums, and bundle/review gate are now in release.yaml alongside the production publish steps (attestation, softprops, deploy-docs, check-install-script). Remove the `always()` + `skipped` conditionals by making get-previous-release always run (~10s read-only gh call) and dropping it from prepare's needs (only publish consumes its output).
- Remove unused `tarball_name` output from prepare job - Make `get-previous-release` resilient to fresh repos with no releases - Add `permissions: contents: read` to `check-install-script` job - Add checksum file count validation before `sha256sum --check` - Clarify macOS universal binary condition with explicit trigger paths - Add `|| github.sha` fallback to `target_commitish` for tag releases - Trim `deploy-docs` permissions to only `contents: write`
- Remove dead code: unused `id: bundle` and `tarball_name` step output
- Add explicit `if` on publish job to tolerate get-previous-release failure
- Fix double ${{ }} evaluation in review job condition
- Increase artifact retention from 2 to 5 days for manual approval window
- Add `opened` to PR event types for pre-labeled PRs
- Enforce exact binary count (4) for tag releases to catch partial builds
4c9c655 to
350c5df
Compare
Summary
release.yamlinto a 3-stage pipeline: build → prepare & review → publishpermissions: {}baselinepublish-unit-test-result-actionto v2.23.0 to fix transitive pinning (the previous commit hash resolved to an unpinned version)Release workflow stages
label-check→prepare-matrix→build(5-platform matrix) +get-previous-releasedeploy-docs+check-install-script(tag-only)Safety on PR
Requires
ci:releaselabel. Runs the full pipeline as a dry-run:github.ref_type != 'tag')github.event_name == 'pull_request')github.ref_type != 'tag')Test plan
ci:releaselabel and verify the full pipeline runs end-to-end as dry-run