To automate publishing after a release PR is merged, use a GitHub Actions workflow. This workflow reacts to pull_request events being closed and checks if they were merged.
Security Requirements for the Workflow:
- Use
pull_request, not pull_request_target, to ensure the workflow runs code from your default branch. - Include a check
github.event.pull_request.head.repo.full_name == github.repository to prevent forks from triggering the workflow. - Ensure the branch name starts with
release/. - Use
contents: write and id-token: write permissions.
Workflow Logic:
- Checks if the PR was merged and originated from the local repo.
- Checks out the merge commit (not the PR head) to ensure
package.json has the bumped version. - Creates a Git tag based on the version found in
package.json. - Generates release notes using
changelogithub. - Publishes to npm using OIDC trusted publishing (requires npm CLI >= 11.5.1).
# .github/workflows/release-pr.yml
name: Release (PR merged)
on:
pull_request:
types: [closed]
jobs:
release:
if: >-
github.event.pull_request.merged == true &&
github.event.pull_request.head.repo.full_name == github.repository &&
startsWith(github.event.pull_request.head.ref, 'release/')
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
fetch-depth: 0
- uses: actions/setup-node@v5
with:
node-version: 22
registry-url: https://registry.npmjs.org
- name: Read version
id: version
run: echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT"
- name: Create tag
uses: actions/github-script@v8
with:
script: |
await github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `refs/tags/v${{ steps.version.outputs.version }}`,
sha: context.payload.pull_request.merge_commit_sha,
})
- run: npm ci
- run: npm run build --if-present
- run: npx changelogithub
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: npm publish