From 1e7b9205d345ca04dc60e1a0ed9aaa8abbd7e0b8 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 7 Sep 2026 02:14:14 -0700 Subject: [PATCH] fix: accept GitHub release verifier workflow paths --- docs/RELEASING.md | 2 ++ scripts/require-published-verifier.sh | 9 ++++++-- scripts/test-release.sh | 32 +++++++++++++++++++-------- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/docs/RELEASING.md b/docs/RELEASING.md index f0ceb35..98dc11d 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -106,6 +106,8 @@ scripts/update-homebrew.sh vX.Y.Z That helper redownloads and re-verifies the published release, requires the exact successful published verifier and both native jobs, and rejects any verifier run that did not begin strictly later than the current release and every current asset's last update. Immediately before its sole tap mutation, it repeats those checks and byte-compares every proof and asset. It renders `Formula/remindctl.rb` from the current tap blob using the locally verified archive SHA-256, updates that exact blob atomically, updates Homebrew, checks the canonical formula homepage/URL/version/checksum, installs or upgrades, runs `brew test`, and repeats the exact signed-binary verification against the installed binary. +The published verifier accepts GitHub's bare `.github/workflows/release.yml` run path or the same path qualified with `@main`. It independently requires `head_branch` to be `main` and `head_sha` to equal the release's exact source commit; a path alone is never branch or source proof. + ## Closeout - Confirm GitHub Release notes match the finalized changelog section and exactly three assets exist. diff --git a/scripts/require-published-verifier.sh b/scripts/require-published-verifier.sh index fd509d9..a31cbaa 100755 --- a/scripts/require-published-verifier.sh +++ b/scripts/require-published-verifier.sh @@ -90,7 +90,12 @@ for page in run_pages: else: raise SystemExit("malformed workflow-run response") candidates = [] -expected_workflow_path = f".github/workflows/release.yml@{default_branch}" +# REST workflow runs use a bare path; also accept the default-branch-qualified +# form. The branch and exact source commit are checked independently below. +expected_workflow_paths = { + ".github/workflows/release.yml", + f".github/workflows/release.yml@{default_branch}", +} for run in runs: started_raw = run.get("run_started_at") if not isinstance(started_raw, str): @@ -100,7 +105,7 @@ for run in runs: run.get("event") == "workflow_dispatch" and run.get("head_sha") == source_commit and run.get("head_branch") == default_branch - and run.get("path") == expected_workflow_path + and run.get("path") in expected_workflow_paths and run.get("display_title") == f"verify published {tag}" and run.get("status") == "completed" and run.get("conclusion") == "success" diff --git a/scripts/test-release.sh b/scripts/test-release.sh index 22c1e67..a7d4b82 100755 --- a/scripts/test-release.sh +++ b/scripts/test-release.sh @@ -209,12 +209,18 @@ PY exit 0 fi if [[ "$*" == *"actions/workflows/release.yml/runs"* ]]; then - workflow_path=".github/workflows/release.yml@main" - if [[ "${GH_MODE:-valid}" == "bare-workflow-path" ]]; then - workflow_path=".github/workflows/release.yml" - fi - printf '[{"workflow_runs":[{"id":456,"event":"workflow_dispatch","head_branch":"main","head_sha":"%s","path":"%s","display_title":"verify published %s","status":"completed","conclusion":"success","run_started_at":"2026-07-09T12:01:00Z","html_url":"https://example.invalid/run/456"}]}]\n' \ - "${MOCK_SOURCE_COMMIT:?}" "$workflow_path" "${MOCK_TAG:-v0.3.3}" + workflow_path=".github/workflows/release.yml" + workflow_branch="main" + workflow_commit="${MOCK_SOURCE_COMMIT:?}" + case "${GH_MODE:-valid}" in + qualified-workflow-path) workflow_path=".github/workflows/release.yml@main" ;; + wrong-workflow-path) workflow_path=".github/workflows/other.yml" ;; + wrong-workflow-qualifier) workflow_path=".github/workflows/release.yml@feature" ;; + wrong-workflow-branch) workflow_branch="feature" ;; + wrong-workflow-commit) workflow_commit="ffffffffffffffffffffffffffffffffffffffff" ;; + esac + printf '[{"workflow_runs":[{"id":456,"event":"workflow_dispatch","head_branch":"%s","head_sha":"%s","path":"%s","display_title":"verify published %s","status":"completed","conclusion":"success","run_started_at":"2026-07-09T12:01:00Z","html_url":"https://example.invalid/run/456"}]}]\n' \ + "$workflow_branch" "$workflow_commit" "$workflow_path" "${MOCK_TAG:-v0.3.3}" exit 0 fi if [[ "$*" == *"actions/runs/456/jobs"* ]]; then @@ -584,12 +590,20 @@ expect_failure "missing native Intel verifier" env \ MOCK_RELEASE_BODY_FILE="$mock_release_notes" MOCK_ASSET_DIR="$mock_api_assets" \ MOCK_SOURCE_COMMIT="$source_commit" MOCK_TAG_OBJECT="$tag_object" \ "$ROOT/scripts/require-published-verifier.sh" "$TAG" -expect_failure "unqualified published verifier workflow path" env \ - GH_BIN="$work/bin/mock-gh" GH_MODE=bare-workflow-path MOCK_RELEASE_STATE=published \ +env \ + GH_BIN="$work/bin/mock-gh" GH_MODE=qualified-workflow-path MOCK_RELEASE_STATE=published \ MOCK_TAG="$TAG" MOCK_VERSION="$MARKETING_VERSION" \ MOCK_RELEASE_BODY_FILE="$mock_release_notes" MOCK_ASSET_DIR="$mock_api_assets" \ MOCK_SOURCE_COMMIT="$source_commit" MOCK_TAG_OBJECT="$tag_object" \ - "$ROOT/scripts/require-published-verifier.sh" "$TAG" + "$ROOT/scripts/require-published-verifier.sh" "$TAG" >/dev/null +for invalid_mode in wrong-workflow-path wrong-workflow-qualifier wrong-workflow-branch wrong-workflow-commit; do + expect_failure "$invalid_mode published verifier" env \ + GH_BIN="$work/bin/mock-gh" GH_MODE="$invalid_mode" MOCK_RELEASE_STATE=published \ + MOCK_TAG="$TAG" MOCK_VERSION="$MARKETING_VERSION" \ + MOCK_RELEASE_BODY_FILE="$mock_release_notes" MOCK_ASSET_DIR="$mock_api_assets" \ + MOCK_SOURCE_COMMIT="$source_commit" MOCK_TAG_OBJECT="$tag_object" \ + "$ROOT/scripts/require-published-verifier.sh" "$TAG" +done expect_failure "stale published verifier after asset replacement" env \ GH_BIN="$work/bin/mock-gh" GH_MODE=stale-assets MOCK_RELEASE_STATE=published \ MOCK_TAG="$TAG" MOCK_VERSION="$MARKETING_VERSION" \