Skip to content

Release

Release #1

Workflow file for this run

name: Release
# Manually triggered full release: validates the version, runs the test gate,
# publishes the package to npm, creates the vX.Y.Z tag, and publishes a GitHub
# Release whose notes are taken from the matching section of CHANGELOG.md.
#
# The version in package.json must already match the version being released,
# and the CHANGELOG must already contain a "## [X.Y.Z]" section for it.
#
# Publishing uses npm trusted publishing (OIDC): this repository and workflow
# file (release.yaml) must be configured as a trusted publisher for
# @ipregistry/client on npmjs.com. No npm token secret is needed.
on:
workflow_dispatch:
inputs:
version:
description: 'Release version, e.g. 1.2.0 or v1.2.0'
required: true
type: string
permissions:
contents: write
id-token: write # npm trusted publishing and provenance
concurrency:
group: release
cancel-in-progress: false
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: 24.x
- name: Validate version
# The raw input is passed through the environment (never interpolated
# into the shell) and strictly validated before any further use.
env:
VERSION_INPUT: ${{ inputs.version }}
run: |
set -euo pipefail
version="${VERSION_INPUT#v}"
if ! printf '%s' "$version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?$'; then
echo "::error::Invalid version '$VERSION_INPUT'. Expected semver such as 1.2.0 or v1.2.0."
exit 1
fi
echo "VERSION=$version" >> "$GITHUB_ENV"
echo "TAG=v$version" >> "$GITHUB_ENV"
- name: Ensure package.json matches the release version
run: |
set -euo pipefail
pkg_version="$(node -p "require('./package.json').version")"
if [ "$pkg_version" != "$VERSION" ]; then
echo "::error::package.json version is $pkg_version but the release version is $VERSION. Bump package.json first."
exit 1
fi
- name: Ensure tag does not already exist
run: |
set -euo pipefail
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
echo "::error::Tag $TAG already exists locally."
exit 1
fi
if git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; then
echo "::error::Tag $TAG already exists on the remote."
exit 1
fi
- name: Extract changelog section
run: |
set -euo pipefail
awk -v ver="$VERSION" '
$0 ~ ("^## \\[" ver "\\]") { capture=1; next }
capture && (/^## / || /^\[[^][]+\]:[[:space:]]/) { exit }
capture { print }
' CHANGELOG.md | sed -e '/./,$!d' > release-notes.md
if [ ! -s release-notes.md ]; then
echo "::error::No changelog section found for [$VERSION] in CHANGELOG.md."
exit 1
fi
echo "Release notes for $TAG:"
echo "----------------------------------------"
cat release-notes.md
echo "----------------------------------------"
- name: Install dependencies
run: npm install
- name: Lint and build
run: |
npm run lint
npm run build
- name: Run system tests (live API)
env:
IPREGISTRY_API_KEY: ${{ secrets.IPREGISTRY_API_KEY }}
IPREGISTRY_API_KEY_THROTTLED: ${{ secrets.IPREGISTRY_API_KEY_THROTTLED }}
run: |
set -euo pipefail
if [ -z "${IPREGISTRY_API_KEY:-}" ]; then
echo "::error::IPREGISTRY_API_KEY secret is required to run system tests before a release."
exit 1
fi
npm run test:integration
- name: Publish to npm
# Trusted publishing needs npm 11.5.1+; upgrade in case the bundled npm
# is older. Provenance is generated automatically with OIDC.
run: |
npm install -g npm@latest
npm publish --access public
- name: Create tag and GitHub release
# The default Actions token (GITHUB_TOKEN) is blocked by an organization
# rule from creating v*-prefixed tags. Provide a RELEASE_TOKEN secret (a
# PAT owned by a user allowed to create such tags, with Contents: write)
# so the tag and release are created as that user.
env:
GH_TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
prerelease=""
case "$VERSION" in
*-*) prerelease="--prerelease" ;;
esac
gh release create "$TAG" \
--target "$GITHUB_SHA" \
--title "$TAG" \
--notes-file release-notes.md \
$prerelease
echo "Released $TAG: ${{ github.server_url }}/${{ github.repository }}/releases/tag/$TAG"