From c6f8d96220c600516a8388479f27cd8ef8209ab4 Mon Sep 17 00:00:00 2001 From: Justin Carper Date: Wed, 26 Aug 2026 11:06:08 -0500 Subject: [PATCH] ci(release): add workflow_dispatch fallback for tag-push event gaps Push/tag events have intermittently failed to enqueue runs (merges to main after 2026-08-26T14:55Z created no CI runs), blocking tag-triggered publishes. Adds a manual trigger with a required tag input; the job resolves RELEASE_REF_NAME once, checks out the tag's exact commit, and all tag-derived steps use it instead of GITHUB_REF_NAME (mirrors ci.yml's dispatch-scoped model-data-drift pattern). --- .github/workflows/release.yml | 38 ++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d79ec3d..9b16512 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,13 +5,25 @@ on: tags: - "v[0-9]+.[0-9]+.[0-9]+" - "v[0-9]+.[0-9]+.[0-9]+-*" + # Fallback for when a tag push doesn't enqueue a run (GitHub event-delivery + # gaps). Triggers are workflow-wide in Actions, so the tag input is + # re-derived as RELEASE_REF_NAME (see the `ref` step) and every step below + # uses that instead of GITHUB_REF_NAME; the tag is fetched and checked out + # at its exact commit. + workflow_dispatch: + inputs: + tag: + description: "Release tag, e.g. v0.9.0-next.0 (must exist)" + required: true + type: string # Default to no permissions; elevate per-job (least privilege). permissions: {} -# Never run two publishes for the same ref concurrently. +# Never run two publishes for the same release concurrently (dispatch runs +# carry the branch ref, so key the group on the resolved release ref). concurrency: - group: release-${{ github.ref }} + group: release-${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }} cancel-in-progress: false jobs: @@ -22,7 +34,21 @@ jobs: contents: write # create the GitHub Release id-token: write # npm provenance attestation steps: + - name: Resolve release ref + id: ref + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + echo "RELEASE_REF_NAME=${{ inputs.tag }}" >> "$GITHUB_OUTPUT" + else + echo "RELEASE_REF_NAME=$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT" + fi + - uses: actions/checkout@v7 + with: + # Manual runs dispatch from a branch (usually main); check out the + # tag's exact commit so tag and package.json agree. + ref: ${{ steps.ref.outputs.RELEASE_REF_NAME }} + fetch-depth: 0 - name: Set up Node uses: actions/setup-node@v7 @@ -37,9 +63,10 @@ jobs: - name: Verify tag matches package.json version run: | PKG_VERSION="$(node -p "require('./package.json').version")" - TAG_VERSION="${GITHUB_REF_NAME#v}" + TAG_NAME="${{ steps.ref.outputs.RELEASE_REF_NAME }}" + TAG_VERSION="${TAG_NAME#v}" if [[ "$PKG_VERSION" != "$TAG_VERSION" ]]; then - echo "::error::git tag $GITHUB_REF_NAME (=$TAG_VERSION) does not match package.json version $PKG_VERSION" + echo "::error::tag $TAG_NAME (=$TAG_VERSION) does not match package.json version $PKG_VERSION" exit 1 fi @@ -73,7 +100,8 @@ jobs: - name: Classify release from tag id: version run: | - VERSION="${GITHUB_REF_NAME#v}" + TAG_NAME="${{ steps.ref.outputs.RELEASE_REF_NAME }}" + VERSION="${TAG_NAME#v}" echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT" if [[ "$VERSION" == *-* ]]; then echo "NPM_TAG=next" >> "$GITHUB_OUTPUT"