diff --git a/.github/workflows/on-release.yml b/.github/workflows/on-release.yml new file mode 100644 index 00000000..b6c893fe --- /dev/null +++ b/.github/workflows/on-release.yml @@ -0,0 +1,103 @@ +name: On Release + +# Publish target for a stateless-validator release: the two binaries as +# downloads on the GitHub Release page, with a SHA256SUMS file. Runs when the +# release pipeline publishes the Release (release-publish creates it after +# the tag), and on demand for a rehearsal on a tag ref: +# gh workflow run on-release.yml --ref vX.Y.Z -f dry_run=true +# The tag being released is always the run's own ref; there is no tag input. +# +# This file must exist on the release branch: a `release` event resolves the +# workflow from the tagged commit's tree, not from the default branch, and the +# tag sits on the release branch. A branch cut from a default branch that has +# this file carries it; a branch cut before it landed needs it cherry-picked +# (via its own PR — the `release-*` ruleset requires one) before the settle PR +# merges, or the Release publishes with nothing attached and no failed run. +# A rehearsal likewise only works on a tag whose tree has this file. +# +# The internal archive to Artifact Registry stays in release.yaml and +# release-tracing.yaml, which the same tag push fires. + +on: + release: + types: [published] + workflow_dispatch: + inputs: + dry_run: + description: "Build and checksum everything; attach nothing" + required: false + type: boolean + default: true + +# One run per tag: a release run and a dispatch on the same tag both end in a +# `--clobber` upload (delete-then-upload), so overlapping runs would race. +# Never cancel an upload mid-flight; queue instead. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +permissions: + contents: read + +env: + TAG: ${{ github.ref_name }} + # A release event is never a dry run; a dispatch defaults to one. + DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false }} + +jobs: + binaries: + runs-on: ubuntu-24.04 + timeout-minutes: 60 + permissions: + contents: write # release-assets attaches to the Release + steps: + - name: Require a tag ref + run: | + [[ "$GITHUB_REF_TYPE" == "tag" ]] || { echo "::error::this workflow publishes the run's own ref, which must be a tag (got $GITHUB_REF_TYPE $GITHUB_REF_NAME); dispatch it with --ref vX.Y.Z"; exit 1; } + + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + ref: ${{ env.TAG }} + persist-credentials: false + + - uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1 + with: + cache: false + + - uses: foundry-rs/foundry-toolchain@b00af27efadbc7b4ca8b82abbd903b17cc874d2a # v1 + + - name: Build + run: cargo build --release --locked --bin stateless-validator --bin debug-trace-server + + - name: Verify the binaries + # stateless-validator carries a clap version (`#[clap(version)]`), so + # it is a gate: the built binary must report the tag's version, which + # also catches a tag / Cargo.toml mismatch nothing else checks here. + # debug-trace-server has no version flag; `--help` proves it loads and + # runs (a missing library or a crash fails the step) without gating. + run: | + set -euo pipefail + expected="stateless-validator ${TAG#v}" + actual="$(target/release/stateless-validator --version)" + if [[ "$actual" != "$expected" ]]; then + if [[ "$DRY_RUN" == "true" ]]; then + # A rehearsal reports what a real run would refuse, and carries on. + echo "::warning::built binary reports '$actual', expected '$expected' — a real release would stop here" + else + echo "::error::built binary reports '$actual', expected '$expected'; refusing to publish a mislabeled binary" + exit 1 + fi + fi + target/release/debug-trace-server --help > /dev/null + for b in stateless-validator debug-trace-server; do + echo "$b: $(stat -c %s target/release/$b) bytes" + done + echo "$actual" + + - uses: megaeth-labs/.github/.github/actions/release-assets@main + with: + tag: ${{ env.TAG }} + files: | + target/release/stateless-validator + target/release/debug-trace-server + dry_run: ${{ env.DRY_RUN }}