If you've ever released an npm package manually, you know how repetitive it can be. You have to bump the version, update the changelog, create a Git tag, push everything to GitHub, publish to npm, and maybe even draft a release. Mess up one step, and things can get messy fast.
That's why automating this process with GitHub Actions is a game-changer. It takes care of versioning, publishing, and creating releases - all without you lifting a finger. In this post, I'll walk you through setting up a GitHub Action that does it all for you.
Let's dive in! 🚀
🔐 Setup: Creating and Adding Required Tokens
1️⃣ Create an npm token
We need to create and add an npm token in the GitHub repository secrets to publish packages from the workflow.
Follow the official docs: Creating and Viewing Access Tokens
2️⃣ Create a GitHub Repository Secret
In your GitHub repository, go to Settings → Secrets and variables → Actions.
Click on New repository secret.
Name the secret NPM_TOKEN.
Paste the npm token as the secret value.
Click Add secret.
3️⃣ GitHub Token (Created Automatically)
GitHub provides an authentication token for GitHub Actions, allowing secure API requests.
More info: GitHub Token Documentation
🔄 Workflow Overview
📌 Triggering the Action
The action runs when a PR is merged into main and has the release label.
It checks if the PR includes a version bump label (patch, minor, or major).
🔼 Version Bumping
- Based on the label, the script updates the package version using pnpm version.
- The new version is committed and pushed back to the repository. 🏗️ Building and Publishing
- The package is built using pnpm build.
- It is then published to npm using pnpm publish. 🏷️ Creating a GitHub Release
- Once published, the workflow tags the new version and creates a GitHub release.
- This makes the new version easily accessible and trackable for users.
🛠️ GitHub Actions Workflow (YAML)
name: Release & Publish
on:
pull_request:
types:
- closed
branches:
- main
jobs:
release:
if: >-
github.event.pull_request.merged == true &&
contains(join(github.event.pull_request.labels.*.name, ','), 'release')
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "lts/*"
registry-url: "https://registry.npmjs.org/"
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Setup pnpm
run: |
corepack enable
pnpm install
echo "pnpm setup complete."
- name: Determine version bump type
id: version_bump
run: |
labels=("${{ join(github.event.pull_request.labels.*.name, ' ') }}")
if [[ "${labels[@]}" == *patch* ]]; then
echo "RELEASE_TYPE=patch" >> $GITHUB_ENV
elif [[ "${labels[@]}" == *minor* ]]; then
echo "RELEASE_TYPE=minor" >> $GITHUB_ENV
elif [[ "${labels[@]}" == *major* ]]; then
echo "RELEASE_TYPE=major" >> $GITHUB_ENV
else
echo "No valid release label found. Exiting..."
exit 1
fi
- name: Setup git user
run: |
git config user.name ""
git config user.email ""
echo "Git user setup complete."
- name: Bump version
run: |
pnpm version ${{ env.RELEASE_TYPE }} --no-git-tag-version
git add package.json
git commit -m "ACTION: Bump version to $(node -p "require('./package.json').version")"
git push
- name: Build package
run: |
pnpm build
- name: Publish package
run: |
pnpm publish --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Get new version
id: package_version
run: echo "VERSION=$(jq -r .version package.json)" >> $GITHUB_ENV
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ env.VERSION }}
name: Release v${{ env.VERSION }}
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
🔧 Key GitHub Actions Used
- actions/checkout@v3 → This action checks out your repository so that the workflow can access your code.
- actions/setup-node@v3 → Sets up a Node.js environment, specifying the version and npm registry authentication.
- softprops/action-gh-release@v1 → Automatically creates a GitHub Release with the new version tag and release notes.
🔀 Alternative: Raising a PR Instead of Pushing Directly to Main
If you don't want the workflow to commit version bumps and changes directly to main, you can configure it to create a pull request instead. This keeps your main branch clean and allows for review before merging.
🛠 Using peter-evans/create-pull-request
Modify your workflow to use the peter-evans/create-pull-request action:
yamlCopy- name: Create Pull Request for Version Bump
uses: peter-evans/create-pull-request@v7
with:
branch: release/version-bump
title: "Bump package version to ${{ env.VERSION }}"
body: "This PR updates the package version to ${{ env.VERSION }}."
commit-message: "chore: bump package version to ${{ env.VERSION }}"
🎉 Wrapping Up
Automating npm package releases with GitHub Actions can save time, reduce errors, and make your workflow seamless. By setting up this automation, you ensure that every release is consistent, well-documented, and instantly available to your users.
Whether you push changes directly to main or prefer a review process with a pull request, this setup gives you flexibility and control over your releases.
Now it's your turn! Have you automated your npm releases? Do you have any improvements or additional steps in your workflow? Drop your thoughts in the comments! 🚀