Skip to content

Release

Release #5

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
increment:
description: "Version increment. Leave empty to infer it from the conventional commits since the last tag."
required: false
default: ""
type: choice
options:
- ""
- patch
- minor
- major
dry-run:
description: "Run release-it without publishing, pushing or tagging."
required: false
default: false
type: boolean
permissions:
contents: write
pull-requests: write
jobs:
release:
name: Publish to npm
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
# conventional-changelog needs the tags and every commit since the
# last one to work out the bump and the changelog entries.
fetch-depth: 0
# No `registry-url` here on purpose: it writes an `.npmrc` holding a
# literal `${NODE_AUTH_TOKEN}`, and the `cache: yarn` probe then runs
# `yarn cache dir`, which dies on "Failed to replace env in config".
# The Release step below writes the token itself.
- uses: actions/setup-node@v7
with:
node-version: 24
cache: yarn
- run: yarn install --frozen-lockfile
- run: yarn lint --max-warnings 0
- run: yarn test
- run: yarn build
# Publishes to npm, pushes the annotated tag and creates the GitHub
# release. The version bump commit stays local, see .release-it.cjs.
- name: Release
id: release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
INCREMENT: ${{ inputs.increment }}
DRY_RUN: ${{ inputs.dry-run }}
run: |
git config user.email "gabrielstaveira@gmail.com"
git config user.name "Gabriel Taveira"
npm config set //registry.npmjs.org/:_authToken "$NPM_TOKEN"
args="--ci"
if [ -n "$INCREMENT" ]; then
args="$args --increment=$INCREMENT"
fi
if [ "$DRY_RUN" = "true" ]; then
args="$args --dry-run"
fi
yarn release $args
echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT"
# master requires status checks and a fresh commit has none, so the bump
# has to arrive as a PR. Merge commit, not squash: the tag points at the
# bump commit, and squashing would rewrite it and leave the tag off
# master's history, which breaks the next release's changelog range.
- name: Land the version bump on master
if: ${{ !inputs.dry-run }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.release.outputs.version }}
BASE: ${{ github.ref_name }}
REPO: ${{ github.repository }}
run: |
branch="chore/release-v$VERSION"
git push origin "HEAD:refs/heads/$branch"
cat > /tmp/pr-body.md <<BODY
Version bump and changelog for [v$VERSION](https://github.com/$REPO/releases/tag/v$VERSION), opened by the release workflow. npm and the tag are already out; this lands the commit the tag points at.
Auto-merges as a merge commit once CI and CodeQL pass. A squash would rewrite the commit and leave the tag outside master's history, which breaks the changelog range for the next release.
BODY
# npm and the tag are already out by this point, so a failure here
# leaves master a version behind the registry. The usual cause is
# "Allow GitHub Actions to create and approve pull requests" being
# off, and the GITHUB_TOKEN has no scope to read that setting, so say
# what to do rather than let a bare GraphQL error stand.
if ! url=$(gh pr create --base "$BASE" --head "$branch" \
--title "chore(release): v$VERSION" --body-file /tmp/pr-body.md 2>&1); then
echo "$url"
echo "::error::v$VERSION is on npm and tagged, but the bump PR could not be opened, so master is behind. The branch $branch is pushed: open a PR from it and merge with a merge commit, not a squash. If the error mentions Actions not being permitted to create pull requests, turn that on under Settings > Actions > General."
exit 1
fi
number="${url##*/}"
echo "opened $url"
# The merge commit lands on master too, so it needs a conventional
# subject. `.release-it.cjs` filters `chore(release)` out of the
# changelog, so neither this nor the bump commit shows up next time.
gh pr merge "$number" --auto --merge --delete-branch \
--subject "chore(release): v$VERSION (#$number)" --body ""