From 086f3f3eac7493ca5b7d7c7fcfc19cc410839314 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 18:58:59 +0000 Subject: [PATCH] ci: add a workflow_dispatch trigger to release-cut The board panel cannot send repository_dispatch from the browser: GitHub requires a real token on /dispatches. Add a manual trigger with mode and version inputs, so a cut runs with gh workflow run release-cut.yml -f mode=dry-run -f version=X.Y.Z GitHub's write-access check is the auth on this path. The inputs get the same mode and version validation, and the signed repository_dispatch path is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01HmnWvKXBkBwojDWM3LvxND --- .github/workflows/release-cut.yml | 44 ++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-cut.yml b/.github/workflows/release-cut.yml index 553b2ee..faefc1d 100644 --- a/.github/workflows/release-cut.yml +++ b/.github/workflows/release-cut.yml @@ -31,6 +31,19 @@ name: Release Cut on: + # Manual trigger, the day-to-day path (gh auth is the credential): + # gh workflow run release-cut.yml --repo -f mode=dry-run -f version=X.Y.Z + workflow_dispatch: + inputs: + mode: + description: "dry-run, or push to open the release PR" + type: choice + options: [dry-run, push] + default: dry-run + version: + description: "Version to cut, N.N.N" + type: string + required: true repository_dispatch: types: [release-cut] push: @@ -45,22 +58,47 @@ concurrency: jobs: verify-token: - name: Verify HMAC signature - if: github.event_name == 'repository_dispatch' + name: Validate request + if: github.event_name != 'push' runs-on: ubuntu-latest outputs: mode: ${{ steps.verify.outputs.mode }} version: ${{ steps.verify.outputs.version }} steps: - - name: Rebuild payload and recompute HMAC + - name: Validate inputs (and the HMAC for repository_dispatch) id: verify env: + EVENT: ${{ github.event_name }} + INPUT_MODE: ${{ inputs.mode }} + INPUT_VERSION: ${{ inputs.version }} + GH_ACTOR: ${{ github.actor }} HMAC_SECRET: ${{ secrets.RELEASE_HMAC_TOKEN }} CLIENT_PAYLOAD: ${{ toJson(github.event.client_payload) }} REPO: ${{ github.repository }} run: | set -euo pipefail + # workflow_dispatch needs write access to the repo, which is the auth + # check here; the HMAC only guards the repository_dispatch path. + if [ "$EVENT" = "workflow_dispatch" ]; then + MODE="$INPUT_MODE" + VER="$INPUT_VERSION" + case "$MODE" in dry-run|push) : ;; *) echo "::error::bad mode: $MODE"; exit 1 ;; esac + if ! [[ "$VER" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::bad version: $VER (expected N.N.N)"; exit 1 + fi + echo "mode=$MODE" >> "$GITHUB_OUTPUT" + echo "version=$VER" >> "$GITHUB_OUTPUT" + { + echo "## Manual dispatch" + echo "- Repo: \`${REPO}\`" + echo "- Mode: \`${MODE}\`" + echo "- Version: \`${VER}\`" + echo "- Actor: \`${GH_ACTOR}\`" + } >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi + if [ -z "${HMAC_SECRET:-}" ]; then echo "::error::RELEASE_HMAC_TOKEN is not set in this repo. The rotator has not run yet, or its GitHub App lacks the Secrets: read and write permission." >&2 exit 1