Automating your npm package publishing process with continuous integration and delivery (CI/CD) ensures that each release passes through a quality gate—your test suite—before publication. At the same time, you can control exactly what ends up in the final published package by excluding test files. In this guide, you’ll learn how to set up CI/CD for a simple npm package—an alphanumeric validator—so that every new GitHub release triggers tests, updates the package version, and automatically publishes a clean package to npm.
Manual npm publishing can be time-consuming and error-prone, particularly as your project grows and gains contributors. By automating the process, you can:
node -v
npm -v
We’ll create a simple alphanumeric-validator
package that exports a function checking if a string is alphanumeric.
Initialize the Project
mkdir alphanumeric-validator cd alphanumeric-validator npm init -y
Update package.json
as needed. For the alphanumeric-validator
, it will look like this.
{
"name": "alphanumeric-validator",
"version": "1.0.0",
"description": "Validates if a string is alphanumeric",
"main": "index.js",
"scripts": {
"test": "jest"
},
"keywords": ["alphanumeric", "validator"],
"author": "Your Name",
"license": "ISC"
}
// index.js
function isAlphaNumeric(str) {
return /^[a-z0-9]+$/i.test(str);
}
module.exports = isAlphaNumeric;
Testing ensures you don’t publish broken code.
Install Jest
npm install --save-dev jest
Create a Test File
mkdir tests
Paste the code below in the tests/index.text.js
file.
// tests/index.test.js
const isAlphaNumeric = require('../index');
test('valid alphanumeric strings return true', () => { expect(isAlphaNumeric('abc123')).toBe(true); });
test('invalid strings return false', () => { expect(isAlphaNumeric('abc123!')).toBe(false); });
Run Tests
npm test
Tests passing? Great. Now, we’ll ensure these tests run in CI before publishing.
Before publishing to Github, you want to exclude the node_modules
. You don’t want to commit node_modules
to version control, as it contains a large number of files that can be regenerated by npm install
.
Create a .gitignore
file at the root of your project:
echo "node_modules" >> .gitignore
This ensures that node_modules
is not tracked by git and won’t be pushed to your repository.
While you will run tests during CI, you don’t want the test files included in your published npm package. This keeps the package clean, has a small bundle size, and ensures only the necessary files are shipped to users.
Create an .npmignore
file in the root folder and add the test file names.
// .npmignore
__tests__
*.test.js // captures all files in the directory with a .test.js extension
This ensures the test files are not included when you run npm publish
.
alphanumeric-validator
repository.
Push Your Code
git init
git add .
git commit -m "Initial commit"
git remote add origin [email protected]:YOUR_USERNAME/alphanumeric-validator.git
git push -u origin main
--access public
flag to make your package public and accessible to users.
npm login
npm publish --access public
Visit https://www.npmjs.com/package/alphanumeric-validator to verify the initial version is live.
You need to configure a workflow that runs on every release event so that when you create a new release (like v1.0.1
):
package.json
to the new version from the release tag.
Create .github/workflows/publish.yml
:
name: Publish Package to npm
# Trigger this workflow whenever a new release is published
on:
release:
types: [published]
# Grant write permissions to the repository contents so we can push version updates
permissions:
contents: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
# Step 1: Check out the repository’s code at the default branch
# This makes your code available for subsequent steps like installing dependencies and running tests.
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.event.repository.default_branch }}
# Step 2: Set up a Node.js environment (Node 20.x) and configure npm to use the official registry
# This ensures we have the right Node.js version and a proper registry URL for installs and publishing.
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
# Step 3: Install dependencies using npm ci
# This ensures a clean, reproducible installation based on package-lock.json.
- name: Install dependencies
run: npm ci
# Step 4: Run your test suite (using the "test" script from package.json)
# If tests fail, the workflow will stop here and not publish a broken version.
- name: Run tests
run: npm test
# Step 5: Update package.json to match the release tag
# The release tag (e.g., v1.0.1) is extracted, and npm version sets package.json version accordingly.
# The --no-git-tag-version flag ensures npm doesn't create its own tags.
# This step keeps package.json's version aligned with the release tag you just created.
- name: Update package.json with release tag
run: |
TAG="${{ github.event.release.tag_name }}"
echo "Updating package.json version to $TAG"
npm version "$TAG" --no-git-tag-version
# Step 6: Commit and push the updated package.json and package-lock.json back to the repo
# This ensures your repository always reflects the exact version published.
# We use the GITHUB_TOKEN to authenticate and the granted write permissions to push changes.
- name: Commit and push version update
run: |
TAG="${{ github.event.release.tag_name }}"
git config user.name "github-actions"
git config user.email "[email protected]"
git add package.json package-lock.json
git commit -m "Update package.json to version $TAG"
git push origin ${{ github.event.repository.default_branch }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Step 7: Publish the new version to npm
# The NODE_AUTH_TOKEN is your npm access token stored as a secret.
# npm publish --access public makes the package available to anyone on npm.
- name: Publish to npm
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
:username
with your actual username)
Go to Settings > Secrets and variables > Actions in your GitHub repository.
Click New Repository Secret and add NPM_TOKEN
.
Let’s say you want to add README.md
for v1.0.1
release, and you have pushed it:
1.0.1
.1.0.1
version to npm, excluding test files.
Integrating GitHub Actions into your npm publishing workflow establishes a great CI/CD pipeline. With each new release, a comprehensive series of tests run, package.json is updated with the correct version, and a streamlined package is published to npm—free of unnecessary files like tests.
This approach saves time, reduces human errors, and enhances the reliability of your releases, making it easier for contributors to see their work go live seamlessly.
A single GitHub release is now all it takes to ship a fully tested, properly versioned package to the npm registry.