From 5931cd50092c514abb2d2f41eb6f9be5c6d7409b Mon Sep 17 00:00:00 2001 From: Elliot Taylor Date: Tue, 14 Jul 2026 10:57:33 +0200 Subject: [PATCH] =?UTF-8?q?Fix=20Release=E2=86=92Publish=20cascade:=20disp?= =?UTF-8?q?atch=20Publish=20instead=20of=20relying=20on=20release=20event?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Release workflow creates the GitHub release with the built-in GITHUB_TOKEN, and GitHub deliberately does not fire release-triggered workflows for those releases (anti-recursion), so Publish never ran and nothing reached npm. Bridge the two via workflow_dispatch, which is exempt from that rule: Release now dispatches Publish with the computed version, and Publish gains a `version` input that publishes the matching tag (blank input still does a dry-run). The release-event trigger stays for manual, human-created releases. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/publish.yml | 32 +++++++++++++++++++++++--------- .github/workflows/release.yml | 7 +++++++ RELEASING.md | 26 ++++++++++++++++++++------ 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1ee24e5..4413c57 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,6 +5,11 @@ on: types: - published workflow_dispatch: + inputs: + version: + description: Version to publish (e.g. 0.3.7). Leave blank for a dry-run. + type: string + default: "" permissions: contents: read @@ -51,12 +56,12 @@ jobs: run: npm pack --dry-run - name: Dry-run publish - if: github.event_name == 'workflow_dispatch' + if: github.event_name == 'workflow_dispatch' && inputs.version == '' run: npm publish --access public --provenance --dry-run publish: name: Publish to npm - if: github.event_name == 'release' + if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.version != '') needs: validate runs-on: ubuntu-latest environment: @@ -64,8 +69,22 @@ jobs: url: ${{ steps.version.outputs.url }} steps: + - name: Resolve version + id: version + run: | + if [ "${{ github.event_name }}" = "release" ]; then + version="${GITHUB_REF_NAME#v}" + else + version="${{ inputs.version }}" + fi + echo "Publishing version $version" + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "url=https://www.npmjs.com/package/@patchstack/connect/v/$version" >> "$GITHUB_OUTPUT" + - name: Checkout uses: actions/checkout@v4 + with: + ref: refs/tags/v${{ steps.version.outputs.version }} - name: Setup Node uses: actions/setup-node@v4 @@ -77,13 +96,8 @@ jobs: - name: Install dependencies run: npm ci - - name: Set version from release tag - id: version - run: | - version="${GITHUB_REF_NAME#v}" - echo "Publishing version $version (from tag $GITHUB_REF_NAME)" - npm version "$version" --no-git-tag-version --allow-same-version - echo "url=https://www.npmjs.com/package/@patchstack/connect/v/$version" >> "$GITHUB_OUTPUT" + - name: Set package version + run: npm version "${{ steps.version.outputs.version }}" --no-git-tag-version --allow-same-version - name: Build run: npm run build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5bda2df..74417d1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,7 @@ on: permissions: contents: write + actions: write concurrency: group: release @@ -56,3 +57,9 @@ jobs: --target "${{ github.sha }}" \ --title "v${{ steps.version.outputs.next }}" \ --generate-notes + + - name: Trigger publish + env: + GH_TOKEN: ${{ github.token }} + run: | + gh workflow run publish.yml -f version="${{ steps.version.outputs.next }}" diff --git a/RELEASING.md b/RELEASING.md index 0a54e4e..abf8263 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -13,9 +13,10 @@ be bumped before a release. Run the **`Release`** workflow from the Actions tab (or `gh` below) and pick a `bump` — `patch`, `minor`, or `major`. It reads the current `latest` from npm, -computes the next semver version, and cuts the GitHub release + tag on the -current `main`. That release then triggers `Publish`, which validates -(typecheck, test, build, `npm pack`) and publishes to npm with provenance. +computes the next semver version, cuts the GitHub release + tag on the current +`main`, and then dispatches `Publish` for that version. `Publish` validates +(typecheck, test, build, `npm pack`) and publishes to npm with provenance, +recording a deployment to the `npm` environment linked to the published version. ```bash gh workflow run Release -f bump=patch @@ -24,9 +25,16 @@ gh workflow run Release -f bump=patch No version math, no `npm view` lookup, no chance of colliding with an existing version — the workflow does all of that. +`Release` triggers `Publish` explicitly via `workflow_dispatch` rather than +relying on the release event. This is deliberate: GitHub does **not** fire +`release`-triggered workflows for releases created by the built-in +`GITHUB_TOKEN` (an anti-recursion safeguard), and `workflow_dispatch` is the +one event type that is exempt. + ## Manual fallback -You can still cut a release by hand, which triggers `Publish` the same way: +You can still cut a release by hand. Because a human token (not `GITHUB_TOKEN`) +creates it, the release event fires `Publish` on its own: ```bash gh release create v0.3.3 --generate-notes --title "v0.3.3" @@ -34,11 +42,17 @@ gh release create v0.3.3 --generate-notes --title "v0.3.3" or use the GitHub UI (Releases → Draft a new release → new tag `v0.3.3`). +You can also publish an existing tag directly: + +```bash +gh workflow run publish.yml -f version=0.3.3 +``` + ## Notes - Tags must be `vX.Y.Z` (the leading `v` is stripped to get the npm version). - For a manual release, pick a version higher than the current `latest` on npm (`npm view @patchstack/connect version`); npm rejects re-publishing an existing version. The `Release` workflow handles this for you. -- Run the `Publish` workflow via **workflow_dispatch** for a dry-run publish - without cutting a release. +- Run `Publish` via **workflow_dispatch** with a blank `version` for a dry-run + publish without cutting a release.