Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
name: npm-publish

on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: >-
Release tag to publish, for example v12.0.0. Leave empty to run
validation only (dry run).
type: string

permissions:
contents: read

concurrency:
# tag_name grouping - queues actual releases for the same tag
# run_id - fallback value required for group as tag_name might not always be present (manual triggers)
group: npm-publish-${{ github.event.release.tag_name || github.run_id }}
# tag grouping - queues repeat publishes of the same release
# run_id - fallback for validation-only runs, which have no tag
group: npm-publish-${{ inputs.tag || github.run_id }}
cancel-in-progress: false

jobs:
Expand All @@ -21,7 +25,7 @@ jobs:
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.event.release.tag_name || github.ref }}
ref: ${{ inputs.tag || github.ref }}
persist-credentials: false

- uses: actions/setup-node@v6
Expand All @@ -30,11 +34,11 @@ jobs:
cache: npm

- name: verify release version
if: github.event_name == 'release'
if: inputs.tag != ''
run: |
pkg_name=$(jq -r .name package.json)
pkg_version=$(jq -r .version package.json)
tag_version="${GITHUB_EVENT_RELEASE_TAG_NAME#v}"
tag_version="${RELEASE_TAG#v}"
if [ "$pkg_version" != "$tag_version" ]; then
echo "package.json version ($pkg_version) does not match release tag ($tag_version)"
exit 1
Expand All @@ -44,7 +48,7 @@ jobs:
exit 1
fi
env:
GITHUB_EVENT_RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
RELEASE_TAG: ${{ inputs.tag }}

- name: install dependencies
run: npm ci
Expand All @@ -65,7 +69,7 @@ jobs:
publish:
name: publish
needs: validate
if: github.event_name == 'release'
if: inputs.tag != ''
runs-on: ubuntu-latest
environment: Publish
permissions:
Expand All @@ -74,7 +78,7 @@ jobs:
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.event.release.tag_name }}
ref: ${{ inputs.tag }}
persist-credentials: false

- uses: actions/setup-node@v6
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,28 @@ permissions:
contents: write
issues: write
pull-requests: write
# Releases created with GITHUB_TOKEN cannot trigger other workflows, so
# npm-publish is dispatched explicitly below. workflow_dispatch is one of the
# few events that always creates a run, even from GITHUB_TOKEN.
actions: write
name: release-please
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: googleapis/release-please-action@v5
id: release
with:
token: ${{ secrets.GITHUB_TOKEN }}
release-type: node
target-branch: ${{ github.ref_name }}

# releases_created is only true on the run that tagged a release, which is
# the run triggered by merging the release PR.
- name: trigger npm-publish
if: steps.release.outputs.releases_created == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
TAG: ${{ steps.release.outputs.tag_name }}
run: gh workflow run npm-publish.yml --ref "$GITHUB_REF_NAME" -f tag="$TAG"
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ Releases are automated with [release-please](https://github.com/googleapis/relea
2. `release-please` opens or updates a **Release PR** with the version bump and changelog.
3. Review and merge the Release PR when ready to ship.
4. `release-please` creates a GitHub Release and version tag (for example `v11.3.0`).
5. The `npm-publish` workflow runs automatically, re-runs tests, validates the package contents (`npm pack --dry-run`), then pauses at the `Publish` environment for reviewer approval.
6. After approval, the package is published to npm via [Trusted Publishing](https://docs.npmjs.com/trusted-publishers) (OIDC). Do not run `npm publish` manually.
5. The same `release-please` run then dispatches `npm-publish` with that tag. It re-runs tests, validates the package contents (`npm pack --dry-run`), then pauses at the `Publish` environment for reviewer approval.
6. After approval, the package is published to npm via [Trusted Publishing](https://docs.npmjs.com/trusted-publishers) (OIDC).

### Dry run

To validate the publish workflow without publishing, run **Actions → npm-publish → Run workflow**. This runs tests and `npm pack --dry-run`.
To validate the publish workflow without publishing, run **Actions → npm-publish → Run workflow** and leave `tag` empty. This runs tests and `npm pack --dry-run`, and skips the publish job.

### Retrying a failed run

If `npm-publish` fails, use **Re-run jobs** on the failed run itself (Actions tab) — it replays the same release/tag, so there's no need to cut a new one. This is safe even if `publish` partially ran, since `validate` checks whether the version is already on npm before continuing.
If `npm-publish` fails, use **Re-run jobs** on the failed run itself (Actions tab) — it replays the same tag, so there's no need to cut a new one. You can also re-run **Actions → npm-publish → Run workflow** with the same `tag`. Either is safe even if `publish` partially ran, since `validate` checks whether the version is already on npm before continuing.
Loading