From a122bff8996b6ca97dd89f6fd0a670d2692a1eaa Mon Sep 17 00:00:00 2001 From: Elliot Taylor Date: Tue, 14 Jul 2026 10:38:22 +0200 Subject: [PATCH] Add one-click Release workflow and surface publishes as GitHub deployments Add a dispatch-triggered Release workflow that computes the next semver version from the npm latest and cuts the tag + GitHub release, removing the manual version-picking step. Split Publish into validate and publish jobs so only real releases record a deployment to the npm environment, now linked to the exact published version. Update RELEASING.md to make the new flow the recommended path. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/publish.yml | 47 +++++++++++++++++++++------- .github/workflows/release.yml | 58 +++++++++++++++++++++++++++++++++++ RELEASING.md | 28 ++++++++++++----- 3 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6fbd671..1ee24e5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,10 +15,9 @@ concurrency: cancel-in-progress: false jobs: - publish: - name: Publish package + validate: + name: Validate package runs-on: ubuntu-latest - environment: npm steps: - name: Checkout @@ -39,13 +38,6 @@ jobs: - name: Install dependencies run: npm ci - - name: Set version from release tag - if: github.event_name == 'release' - 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 - - name: Typecheck run: npm run typecheck @@ -62,6 +54,39 @@ jobs: if: github.event_name == 'workflow_dispatch' run: npm publish --access public --provenance --dry-run + publish: + name: Publish to npm + if: github.event_name == 'release' + needs: validate + runs-on: ubuntu-latest + environment: + name: npm + url: ${{ steps.version.outputs.url }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 24.x + registry-url: https://registry.npmjs.org + cache: npm + + - 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: Build + run: npm run build + - name: Publish to npm - if: github.event_name == 'release' run: npm publish --access public --provenance diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..5bda2df --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,58 @@ +name: Release + +on: + workflow_dispatch: + inputs: + bump: + description: Semver bump to apply over the current npm latest + type: choice + default: patch + options: + - patch + - minor + - major + +permissions: + contents: write + +concurrency: + group: release + cancel-in-progress: false + +jobs: + release: + name: Cut release (${{ inputs.bump }}) + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 24.x + + - name: Compute next version + id: version + run: | + current="$(npm view @patchstack/connect version 2>/dev/null || echo 0.0.0)" + next="$(npx --yes semver -i "${{ inputs.bump }}" "$current")" + echo "Current npm latest: $current" + echo "Next version: $next (bump: ${{ inputs.bump }})" + + if git rev-parse "v$next" >/dev/null 2>&1; then + echo "::error::Tag v$next already exists." + exit 1 + fi + + echo "next=$next" >> "$GITHUB_OUTPUT" + + - name: Create release + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release create "v${{ steps.version.outputs.next }}" \ + --target "${{ github.sha }}" \ + --title "v${{ steps.version.outputs.next }}" \ + --generate-notes diff --git a/RELEASING.md b/RELEASING.md index eca955e..0a54e4e 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -9,9 +9,24 @@ with the published version. `package.json`'s committed `version` is just a placeholder — it does not need to be bumped before a release. -## How to release +## How to release (recommended) -Create a GitHub release with a new tag: +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. + +```bash +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. + +## Manual fallback + +You can still cut a release by hand, which triggers `Publish` the same way: ```bash gh release create v0.3.3 --generate-notes --title "v0.3.3" @@ -19,14 +34,11 @@ 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`). -Publishing the release triggers the `Publish` workflow, which validates -(typecheck, test, build, `npm pack`) and publishes to npm with provenance using -the tag's version. - ## Notes - Tags must be `vX.Y.Z` (the leading `v` is stripped to get the npm version). -- Pick a version higher than the current `latest` on npm (`npm view - @patchstack/connect version`); npm rejects re-publishing an existing 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.