All release/publishing workflows must be deterministic, serialized safely, and strictly gated so that user-facing artifacts are published only after the release candidate has passed the final checks.
Apply this standard when building CI/CD for npm/GitHub releases, backfills, and container/image promotions:
refs/tags/...) and a correct ref type.main).:latest / version tags.skip-release-notes), automation must not delete it on subsequent title edits.Example (pattern for safe promotion ordering):
concurrency:
group: release-${{ github.repository }}
queue: max
jobs:
verify:
runs-on: ubuntu-latest
steps:
# build candidate
# run integration/verification
# run attestations as needed
- name: Verify candidate
run: |
set -euo pipefail
./scripts/verify-release-candidate.sh
promote:
needs: verify
if: ${{ success() }}
steps:
- name: Publish only after success
run: |
set -euo pipefail
# select by digest / candidate reference
./scripts/promote-release.sh
This prevents out-of-order releases, removes dependency on runner/tool bundling, and avoids scenarios where failures leave user-facing tags published even though verification ultimately failed.