How to Publish an Updated Version of an npm Package
TL;DR: What’s typically involved in an npm version release? How can you determine the release process for an existing project? Can project maintainers do anything to make it easier for new contributors?
Recently, I’ve been trying to do a bit more open source work, especially in the form of maintaining Cloud Four’s npm packages. However, as someone relatively new to the Operations side of things, I found myself getting lost about how exactly to release a new version of a package.1
Part of the problem was that these projects were created over many years, with multiple developers involved. Because standards and best practices evolve quickly, each project’s release process was configured differently. Some used a release helper tool like npmpub, others had prepublish
scripts, and others had nothing special at all.
None of this is bad, and I’m not trying to shame any other devs, but it did increase friction. Releasing a new version of any package was a little harder than it needed to be, since I had to figure out what process to use for each.
So I recently spent some time reading the npm docs and familiarizing myself with some of the most common release helper tools. I wanted get a better handle on what’s typically involved in the npm release process.
In this article, I’ll cover the most common release steps, how to determine the release process for an existing project, and recommendations for project maintainers.
- The “Standard” Release Process
- Determining How to Release an Existing Project
- Recommendations for Project Maintainers
- Resources
The “Standard” Release Process
Here is my understanding of the steps that are most commonly expected to be run when publishing a new version of an npm package.
- Safety Checks:
git pull
git status
npm ci
npm test
- Prepare the Release:
npm run build
- Update the Changelog
- Update the Version Number:
npm version
- Or by hand:
- Update version in
package.json
&package-lock.json
git commit -am '2.0.0'
git tag v2.0.0
- Update version in
- Publish to npm:
npm publish
- Publish to Git:
git push
git push --tags
- Create a GitHub Release (optional)
1. Safety Checks
The following steps are optional, but could save you a lot of trouble having to publish a bug fix after a bad release.
git pull
Check that you’re working on the master
branch (or the branch you publish from), and ensure you’re not missing any commits from the repo.
git status
Confirm that you’ve got a clean working directory. You don’t want to see any uncommitted changes.
npm ci
The ci
command will run a clean install of your Node modules, so you can validate that the versions you specify install cleanly.
npm test
If you have tests or linters, run them now, to make sure you don’t have any release-blocking problems in your code.
Note: It’s common to add some or all of these steps into a preversion
or prepublishOnly
script in package.json
. These scripts will be automatically run before the npm version
or npm publish
commands respectively. This will ensure that nobody forgets to run them.
2. Prepare the Release
If your package includes any resources that need to be compiled, now’s the time to run your build steps.
npm run build
This is the most common compile command, but your package may be configured differently. Check the documentation!
Note: If a build is required for your package, it’s a very good idea to add the build step to a prepublishOnly
script. That way you can avoid the embarrassing situation of someone publishing a new version that doesn’t actually contain the new code.
3. Update the Changelog
I feel very strongly that every project should keep a changelog. It allows users to see what noteworthy changes were made between releases, so it should be updated every time you publish a new version.
If your project already has a changelog, add a new entry for the version you’re about to publish. Follow the existing style, and remember that changelogs are for humans, not machines. List what changed with this release, and why, with special attention paid to deprecations and breaking changes.
If your project doesn’t use a changelog, or prefers GitHub Releases (see below), consider adding one as part of this release. It’s never too late!
4. Update the Version Number
The easiest way to update your version number is to use the handy npm version
command. This will automatically update the version number in package.json
and package-lock.json
. It will also create a version commit and a new Git tag.
You’ll need to tell npm what type of release this is by specifying a valid semantic versioning type, such as major
, minor
, or patch
. Then npm will update the appropriate part of your version number:
npm version minor
You could do this all by hand, if you prefer. For example, here’s what you would do for a minor release from version 2.0.0:
- Update the version number to 2.1.0 in
package.json
&package-lock.json
. - Commit the changes you just made:
git commit -am 'Bump version to 2.1.0'
- Make a new Git tag:
git tag v2.1.0
However, an advantage of using npm version
is you can configure it to run additional commands. Add a preversion
or postversion
script to package.json
with the commands you’d like to run before or after bumping the version.
For example, you could run the safety checks mentioned above in a preversion
script so npm will only bump the version if the tests pass.
5. Publish to npm
Finally, once you’re confident this version is ready to release, you can publish the updated package to npm.
npm publish
The publish
command will add the updated package to the npm registry.
6. Publish to Git
You’ll also want to make sure your changes are promoted to the Git repo.
git push
Unfortunately, the push
command doesn’t send tags. To push the new tag you made, you’ll need to run the push
command again with the --tags
flag:
git push --tags
Note: It can be a good idea to add these commands to a postpublish
script, so that when someone releases code to npm, it’s automatically merged to Git at the same time.
7. Create a GitHub Release (optional)
GitHub Releases are a proprietary feature that GitHub built on top of standard Git tags. They allow you to attach release notes to a tag.
I recommend that you copy the changes you made to the changelog earlier as the notes for your GitHub Release.
Some devs prefer GitHub Releases over maintaining a dedicated changelog file. However, there are some downsides to this approach. Releases only exist in the GitHub web interface and are not very easy to discover. They are also a GitHub-only feature, so if you ever migrate to another version control provider, you’ll lose your release history.
Of course, there’s no reason you can’t use both!
That Seems Like a Lot of Work!
No kidding! Judging by the many tools designed to help with releases, lots of devs feel the same way. Some of the most popular include semantic-release, release-it, np, npmpub, and bump.
Each tool addresses some of the pain points in the release process. You should evaluate helper tools carefully before deciding to add any to your process. Be aware that while a tool may automate some of the process, you’ll need to add clear documentation explaining how to use it, ideally in a contributing guide.
There’s no clear industry standard, and many projects don’t have documented release procedures. This can make figuring out how to publish a new version tricky in a project you’re not familiar with.
Which brings us to the next topic:
Determining How to Release an Existing Project
Now that we have a good understanding of the typical release steps, let’s talk about how to evaluate an existing project to determine what release process it uses.
1. Check the README
and CONTRIBUTING
Files
Start by looking at the README
file. You’re not likely to find the release process here, because it’s intended for users, not contributors. However, there may be a link to a contributing guide.
If there isn’t, check for a CONTRIBUTING.md
file. Most projects keep this in the root folder, but some move it to a docs
folder or even the .github
folder.
If you can’t find a contributing guide, or if it doesn’t document the release process, it’s time for the next step.
2. Ask Other Project Members
Someone has been handling releases before you. See if you can get in touch with the team and ask if anyone knows about the release process.
If you’re unsure who to ask, look at the commit history to see who’s name is on the last commit that bumped the version number.
If no one knows, or if you can’t get in touch with previous releasers, then it’s time for some detective work.
3. Look for Scripts in package.json
Start by looking in package.json
for the scripts
section. See if any of the scripts seem relevant.
Sometimes you’ll find a release
or publish
script that runs a release helper tool. If so, then you’ll want to try using this for releasing.
Or you might find some of the publishing lifecycle scripts, like preversion
, postversion
, prepublishOnly
, or postpublish
. If so, that’s a good indication that you’ll use the standard process outlined above, including the npm version
and npm publish
commands.
4. If All Else Fails, Follow the “Standard” Release Process
If you can’t find any documentation, and nothing indicates there’s anything custom about the release process, then you can follow the “standard” process outlined above.
Once you manage to get a successful release, it’s time to start thinking about the future. How can you improve the process for this project?
Recommendations for Project Maintainers
Here are some things you can do to make it easier for new contributors to understand how to release a new version of your project.
Document Your Release Process in a Contributing Guide
It’s a good idea to add a contributing guide to your project, so that new contributors can learn anything they need to know about how to work on it. That includes the release process.
GitHub has a good introduction to adding a contributing guide. Typically, this lives in a CONTRIBUTING.md
file to make it easy to find. If you put it anywhere other than the root folder, considering adding a link in the README
as well.
Document Your Release Helper Tools
Helper tools are great, but don’t assume that every contributor will be familiar with them. At the very least, add a link to the tool’s documentation from your contributing guide. Even better, explain exactly what steps you expect contributors to follow when using the tool.
For example, if you’re using a release tool that automates the process of updating the changelog, the contributing guide should explain what the result should look like and what a contributor using the tool can expect.
Choose Release Helper Tools Carefully
In general, there’s a trade-off between a tool that abstracts some of the work away from contributors, and the extra overhead of those contributors having to learn how to use it.
If your team has more inexperienced contributors, a multiple-step release process could intimidate them or cause them to hesitate to release a new version. In this case, a helper tool might make things easier by reducing the number of steps they need to follow, and ensure they don’t skip a step by mistake.
On the other hand, if your team is mostly experienced open-source contributors, a release tool holding their hand might seem more annoying than helpful. In that case, limiting your helpers to a simple preversion
or prepublishOnly
script might make things easier, by hewing closer to the “standard” release process.
In the end, the best release process is the one that suits your team.
Resources
- How to make a beautiful, tiny npm package and publish it
- Releasing a new version to GitHub
- Releasing a new version to npm
- Automating releases with semantic-release
- Easy automatic npm publishes
- These 6 essential tools will release, version, and maintain your NPM modules for you
- How to publish packages to npm (the way the industry does things)
- Learn how to develop and publish an npm package
- How to test your new NPM module without publishing it every 5 minutes
Footnotes
As an aside, until recently, I mistakenly used the words packages and modules interchangeably. A package is a file or directory that is described by a
package.json
file. A module is any file or directory (typically found in thenode_modules
directory) that can be loaded by the Noderequire()
function. So the npm registry contains packages, many of which contain Node modules. ↩︎