[codex] Bind release publishers to immutable tags (BUZZ-SEC-039) (#1914)

This commit is contained in:
Jordan Mecom
2026-07-17 08:52:57 -07:00
committed by GitHub
parent 57316a378e
commit 2310b03f9d
6 changed files with 177 additions and 46 deletions
@@ -133,17 +133,23 @@ jobs:
VERSION: ${{ steps.release.outputs.version }}
TAG: ${{ steps.release.outputs.tag }}
run: |
# Both workflows take the bare version (for the build) and the tag ref
# (so the dispatch builds the tagged commit, not main).
# Run the workflow itself at the immutable tag so github.sha, OIDC,
# provenance, and the checked-out source all name the release commit.
extra_args=()
case "$DISPATCH" in
release) WORKFLOW="release.yml" ;;
docker) WORKFLOW="docker.yml" ;;
helm-chart) WORKFLOW="helm-chart.yml" ;;
push-gateway-helm-chart) WORKFLOW="push-gateway-helm-chart.yml" ;;
helm-chart)
WORKFLOW="helm-chart.yml"
extra_args=(-f "ref=$TAG") ;;
push-gateway-helm-chart)
WORKFLOW="push-gateway-helm-chart.yml"
extra_args=(-f "ref=$TAG") ;;
*)
echo "::error::Unhandled dispatch target: '$DISPATCH'"
exit 1 ;;
esac
gh workflow run "$WORKFLOW" \
--ref "$TAG" \
-f version="$VERSION" \
-f ref="$TAG"
"${extra_args[@]}"
+2
View File
@@ -55,6 +55,8 @@ jobs:
mobile:
- 'mobile/**'
- '.github/workflows/ci.yml'
- name: Release workflow source contract
run: scripts/test-release-ref-contract.sh
rust-lint:
name: Rust Lint
+23 -17
View File
@@ -18,22 +18,19 @@ name: Docker image
# - push tags relay-v*.*.* → :{version} + :{major}.{minor} + :{major}
# (+ :latest for stable, NOT for prereleases)
# - pull_request → build only (no push), cache stays warm
# - workflow_dispatch → manual canary (no inputs), or relay-tag rescue
# dispatch with version+ref inputs (see below)
# - workflow_dispatch → relay-tag rescue, dispatched at the tag itself
#
# Why workflow_dispatch carries version/ref inputs:
# Why workflow_dispatch carries a version input:
# auto-tag-on-release-pr-merge.yml pushes relay-v* with the default
# GITHUB_TOKEN, which GitHub deliberately does NOT let fire on:push triggers
# (recursion guard). So the push:tags trigger above never runs for releases.
# auto-tag instead dispatches this workflow with the bare version + tag ref,
# the same rescue release.yml already uses for the desktop lane. On dispatch
# github.ref is `main`, so the tag ref is plumbed through explicitly: checkout
# pins to inputs.ref, and the semver tags take inputs.version via `value=`.
# auto-tag instead dispatches this workflow at the immutable tag and supplies
# the bare version for metadata-action. The workflow rejects a dispatch whose
# github.ref, checked-out HEAD, and relay-v tag do not resolve to one commit.
# On the rescue path inputs.version is already bare (e.g. 0.3.0), so the
# match=^relay-v(.*)$ regex simply no-ops (it warns, leaving the value
# intact) and the bare version flows straight to the semver parser. On a
# real push event value= is empty and the match strips relay-v from the ref.
# Inputless canary dispatch (version="", ref=main) renders no semver tag.
#
# The :latest tag tracks the latest STABLE relay release: metadata-action's
# `flavor.latest=auto` (its default) emits :latest only for non-prerelease
@@ -63,11 +60,7 @@ on:
inputs:
version:
description: "Semver version e.g. 0.3.0 (no relay-v prefix) — for relay-tag rescue dispatch"
required: false
ref:
description: "Tag/branch/SHA to build, e.g. relay-v0.3.0"
required: false
default: main
required: true
# One image build per ref; cancel superseded PR builds, but never cancel
# tag/main builds (publishing must not be aborted mid-flight).
@@ -112,11 +105,17 @@ jobs:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
# On workflow_dispatch (relay-tag rescue) build the tagged commit,
# not main. Empty string = default ref for push/PR events.
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || '' }}
fetch-depth: 0
persist-credentials: false
- name: Verify tag-bound release source
if: github.ref_type == 'tag' || github.event_name == 'workflow_dispatch'
env:
INPUT_VERSION: ${{ inputs.version }}
run: |
VERSION="${INPUT_VERSION:-${GITHUB_REF_NAME#relay-v}}"
scripts/verify-release-ref.sh relay-v "$VERSION"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
with:
@@ -327,8 +326,15 @@ jobs:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || '' }}
fetch-depth: 0
persist-credentials: false
- name: Verify tag-bound release source
if: github.ref_type == 'tag' || github.event_name == 'workflow_dispatch'
env:
INPUT_VERSION: ${{ inputs.version }}
run: |
VERSION="${INPUT_VERSION:-${GITHUB_REF_NAME#relay-v}}"
scripts/verify-release-ref.sh relay-v "$VERSION"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
with:
+47 -24
View File
@@ -7,18 +7,12 @@ on:
workflow_dispatch:
inputs:
version:
description: "Semver version (e.g. 0.4.0) — only for manual runs"
required: true
ref:
description: "Branch, tag, or SHA to build — only for manual runs"
default: main
description: "Semver version matching the v-prefixed dispatch tag"
required: true
jobs:
# Shared setup: determine the version and create the release objects all four
# platform jobs upload into. Runs on ubuntu with no OIDC so a branch
# workflow_dispatch can exercise the platform builds without the codesign
# AssumeRole wall that gates the arm64 job.
# Shared setup: verify the immutable release tag, determine the version, and
# create the release objects all four platform jobs upload into.
setup:
name: Setup
if: github.repository == 'block/buzz'
@@ -28,6 +22,7 @@ jobs:
contents: write
outputs:
version: ${{ steps.version.outputs.version }}
source_sha: ${{ steps.source.outputs.source_sha }}
steps:
- name: Determine version
id: version
@@ -51,13 +46,19 @@ jobs:
exit 1
fi
# Checkout at the same ref the platform jobs use so git rev-parse HEAD
# pins the release to the right commit via --target.
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
ref: ${{ github.event_name == 'push' && github.ref || inputs.ref }}
fetch-depth: 0
persist-credentials: false
- name: Verify tag-bound release source
id: source
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
scripts/verify-release-ref.sh v "$VERSION"
echo "source_sha=$(git rev-parse 'HEAD^{commit}')" >> "$GITHUB_OUTPUT"
- name: Create versioned GitHub release
env:
VERSION: ${{ steps.version.outputs.version }}
@@ -108,9 +109,13 @@ jobs:
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
ref: ${{ github.event_name == 'push' && github.ref || inputs.ref }}
ref: ${{ needs.setup.outputs.source_sha }}
fetch-depth: 0
persist-credentials: false
- name: Verify tag-bound release source
run: scripts/verify-release-ref.sh v "$VERSION"
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
- name: Install desktop dependencies
@@ -291,7 +296,7 @@ jobs:
run: gh release upload "v${VERSION}" "$DMG_PATH" --clobber
- name: Upload updater archive to rolling release
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.ref == format('v{0}', inputs.version))
if: github.ref == format('refs/tags/v{0}', needs.setup.outputs.version)
run: |
gh release upload buzz-desktop-latest \
"$ARCHIVE_PATH" \
@@ -320,9 +325,13 @@ jobs:
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
ref: ${{ github.event_name == 'push' && github.ref || inputs.ref }}
ref: ${{ needs.setup.outputs.source_sha }}
fetch-depth: 0
persist-credentials: false
- name: Verify tag-bound release source
run: scripts/verify-release-ref.sh v "$VERSION"
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
- name: Install desktop dependencies
@@ -451,7 +460,7 @@ jobs:
DMG_PATH: ${{ steps.unsigned.outputs.dmg }}
- name: Upload updater archive to rolling release
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.ref == format('v{0}', inputs.version))
if: github.ref == format('refs/tags/v{0}', needs.setup.outputs.version)
run: |
gh release upload buzz-desktop-latest \
"$ARCHIVE_PATH" \
@@ -536,12 +545,18 @@ jobs:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
ref: ${{ github.event_name == 'push' && github.ref || inputs.ref }}
ref: ${{ needs.setup.outputs.source_sha }}
fetch-depth: 0
persist-credentials: false
- name: Mark workspace safe for git (containerized job)
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Verify tag-bound release source
env:
VERSION: ${{ needs.setup.outputs.version }}
run: scripts/verify-release-ref.sh v "$VERSION"
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
@@ -687,7 +702,7 @@ jobs:
--clobber
- name: Upload updater archive to rolling release
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.ref == format('v{0}', inputs.version))
if: github.ref == format('refs/tags/v{0}', needs.setup.outputs.version)
run: |
gh release upload buzz-desktop-latest \
"$ARCHIVE_PATH" \
@@ -714,9 +729,14 @@ jobs:
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
ref: ${{ github.event_name == 'push' && github.ref || inputs.ref }}
ref: ${{ needs.setup.outputs.source_sha }}
fetch-depth: 0
persist-credentials: false
- name: Verify tag-bound release source
shell: bash
run: scripts/verify-release-ref.sh v "$VERSION"
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
targets: ${{ env.TARGET }}
@@ -815,7 +835,7 @@ jobs:
EXE_PATH: ${{ steps.artifacts.outputs.exe }}
- name: Upload updater archive to rolling release
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.ref == format('v{0}', inputs.version))
if: github.ref == format('refs/tags/v{0}', needs.setup.outputs.version)
shell: bash
run: |
gh release upload buzz-desktop-latest \
@@ -829,12 +849,11 @@ jobs:
assemble-manifest:
name: Assemble multi-platform latest.json
# Only tag-backed releases assemble latest.json. Branch workflow_dispatch
# runs skip rolling-release uploads, so their archive URLs never exist.
# Only the tag-bound setup path can reach this job.
if: |
always() &&
needs.setup.result == 'success' &&
(github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.ref == format('v{0}', inputs.version)))
github.ref == format('refs/tags/v{0}', needs.setup.outputs.version)
runs-on: ubuntu-latest
needs: [setup, release, release-macos-x64, release-linux, release-windows]
timeout-minutes: 10
@@ -846,9 +865,13 @@ jobs:
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
ref: ${{ github.event_name == 'push' && github.ref || inputs.ref }}
ref: ${{ needs.setup.outputs.source_sha }}
fetch-depth: 0
persist-credentials: false
- name: Verify tag-bound release source
run: scripts/verify-release-ref.sh v "$VERSION"
- name: Write signature files
env:
RESULT_ARM64: ${{ needs.release.result }}