github-actions
GitHub Actions Publish to npm
GitHub Actions workflow to automatically publish packages to npm on release with provenance, version validation, and scoped access.
Overview
A GitHub Actions workflow that publishes your package to npm when a GitHub release is created. Includes test validation, version consistency checks, npm provenance for supply chain security, and support for scoped packages.
Configuration
# .github/workflows/publish.yml
name: Publish to npm
on:
release:
types: [published] # Trigger on GitHub release creation
# Only one publish at a time
concurrency:
group: npm-publish
cancel-in-progress: false # Never cancel a publish in progress
jobs:
validate:
name: Validate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm ci
- name: Run linting
run: npm run lint
- name: Run type checking
run: npx tsc --noEmit
- name: Run tests
run: npm test -- --ci
- name: Build package
run: npm run build
# Verify the version in package.json matches the release tag
- name: Validate version
run: |
PACKAGE_VERSION=$(node -p "require('./package.json').version")
TAG_VERSION="${{ github.event.release.tag_name }}"
# Strip 'v' prefix from tag if present
TAG_VERSION="${TAG_VERSION#v}"
if [ "$PACKAGE_VERSION" != "$TAG_VERSION" ]; then
echo "::error::Version mismatch: package.json=$PACKAGE_VERSION, tag=$TAG_VERSION"
exit 1
fi
echo "Version validated: $PACKAGE_VERSION"
publish:
name: Publish
runs-on: ubuntu-latest
needs: validate
permissions:
contents: read
id-token: write # Required for npm provenance
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
registry-url: https://registry.npmjs.org # Configure npm registry
- name: Install dependencies
run: npm ci
- name: Build package
run: npm run build
# Verify package contents before publishing
- name: Check package contents
run: |
npm pack --dry-run
echo "---"
echo "Package size:"
npm pack 2>&1 | tail -1
# Publish with provenance (supply chain attestation)
- name: Publish to npm
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
# Verify the package was published
- name: Verify publication
run: |
sleep 10
PACKAGE_NAME=$(node -p "require('./package.json').name")
PACKAGE_VERSION=$(node -p "require('./package.json').version")
npm view "$PACKAGE_NAME@$PACKAGE_VERSION" version
echo "Successfully published $PACKAGE_NAME@$PACKAGE_VERSION"
Key Options Explained
release: types: [published]— Triggers only when a GitHub release is published (not drafted). Gives you manual control over when packages ship.cancel-in-progress: false— Never interrupts an ongoing publish. A half-published package could leave the registry in a bad state.id-token: write— Grants permission to request an OIDC token from GitHub, which npm uses to create provenance attestations linking the package to its source.--provenance— Generates a signed build provenance statement (SLSA) proving the package was built from the claimed source repo via GitHub Actions.--access public— Required for scoped packages (@org/package) to be publicly accessible. Unscoped packages are public by default.registry-url— Thesetup-nodeaction automatically configures.npmrcwith auth for this registry usingNODE_AUTH_TOKEN.- Version validation — Compares
package.jsonversion with the Git tag to catch mismatches before publishing.
Common Modifications
- Scoped packages: For private scoped packages, change
--access publicto--access restricted. - Pre-release tags: Add
npm publish --tag betafor pre-release versions. Users install withnpm install package@beta. - Monorepo: Use
npx changeset publishornpx lerna publish from-packagefor multi-package publishing. - GitHub Packages: Change
registry-urltohttps://npm.pkg.github.comand useGITHUB_TOKENinstead ofNPM_TOKEN. - Changelog automation: Add a step to extract release notes from
CHANGELOG.mdand include them in the GitHub release body.