How to publish Node.js packages with Travis CI and packagecloud
This is a guest blog post from Julio Capote, cofounder at packagecloud.
This post will show you how to take advantage of Node.js Scoped Packages and how to setup continuous delivery for them using Travis CI and packagecloud.
We’ll build a software delivery pipeline that looks like the following:
-
Tagged commits are pushed to Github
-
Tests are run on Travis CI
-
Successfully built artifacts are deployed to an NPM registry on packagecloud
Example Scenario
For our completely fictitious example, imagine we have various Node.js micro-services using the popular left-pad NPM library. However, it appears that this library exhibits mysterious behavior that has only been witnessed in production.
Using an NPM registry to distribute changes
The natural approach to this problem is to deploy a bunch of extra logging in hopes that the mystery reveals itself. But, since several of our systems use this library in concert, it would be impractical to vendor our modifications inside every project that needs them. Instead, we’ll push our modified package to our own NPM registry, which our services are already configured to use.
Creating an NPM registry
If you don’t have a packagecloud account already, you can get your own hosted NPM registry by signing up at packagecloud.io.
Scoped Node.js Packages
Node.js Scoped Packages are a feature that lets us take put any package under our own namespace, so it’s always clear which fork of the library is being used.
To scope a Node.js package fork it on GitHub, then prepend @your-name/
to the name
field of the package.json
file, like so:
{
"name": "@computology/left-pad",
"version": "1.2.2",
...
}
You can follow along with this post by cloning our modified library over at computology/left-pad.
Configure Travis CI
We’ll set up a continuous delivery pipeline on Travis CI to make it easy to ship new versions of our package to all of our services.
Sign into Travis CI with our Github Account
This will let Travis CI access our repositories.
Enable the repository
Set API_TOKEN
environment variable
Go to your packagecloud API Token page and set your token to the value of an environment variable named API_TOKEN
.
NOTE: Make sure that “Display value in build log” is set to OFF, or else your API token might be revealed to anyone that browses your project!
Configuring our Node.js project for Travis CI
Add a .travis.yml
file
We’ll start by placing a .travis.yml
file into the root of your project.
language: node_js
node_js:
- "9"
deploy:
provider: script
script: "cp .npmrc.template $HOME/.npmrc && npm publish"
skip_cleanup: true
on:
tags: true
notifications:
email:
recipients:
- julio@packagecloud.io
on_success: change
on_failure: always
This file is pretty straightforward, we are telling Travis CI that this is a node_js
project and to load Node.js version “9.x.x”.
Under the deploy:
section, we are using the script provider to run two commands sequentially:
- copy over our
.npmrc.template
(see below) into$HOME/.npmrc
npm publish
Our job doesn’t generate anything that needs cleaning up, so we can speed it up by passing skip_cleanup: true
. The on:
part ensures that we only publish on tagged commits. Finally, under notifications:
we set our notification email and preferences.
Add a .npmrc.template
file
When you run npm publish
, the npm
command for a settings file located at $HOME/.npmrc
, which is where our Travis CI script is configured to copy the following template:
always-auth=true
registry=https://packagecloud.io/capotej/left-pad-example/npm/
//packagecloud.io/capotej/left-pad-example/npm/:_authToken=${API_TOKEN}
You’ll want to replace capotej/left-pad-example
on both lines with your own packagecloud username and repository. But leave the string ${API_TOKEN}
as is, as we want npm
to interpolate this value from the environment variable we set in our Travis CI settings earlier.
Bump the version and push
We’ll be using the npm version
command to bump and tag our package versions.
Using npm version
to bump and tag new versions
Using the patch
argument bumps our package a single patch version and the %s
inside our commit message will be replaced with that version. This command also takes care of creating a git tag
for that version.
npm version patch -m "Bumping to %s"
Push all of our changes to Github (origin):
git push origin master
This will start a build on Travis CI, but it will not deploy.
Because we specified on: tags
in our .travis.yml
file, we have to push up our the git tag to Github (origin) in order to actually deploy our new package:
git push --tags
Using the packagecloud NPM registry
After the build for the tagged commit finishes, we see should hopefully see our scoped package in our packagecloud NPM registry.
Adding the NPM registry to our services
To use our modified package we’ll need configure our NPM registry for any project that depends on it.
npm config set registry https://packagecloud.io/capotej/left-pad-example/npm/
Note: the trailing slash is important!
Scoped NPM registry
You can also pass a scope when configuring a registry, like so:
npm config set @computology:registry https://packagecloud.io/capotej/left-pad-example/npm/
This tells npm
to use this NPM registry only for packages using that scope. This is useful if your project already uses an NPM registry, but would like to keep your scoped packages separate from it.
Depend on the scoped package
To depend on our scoped package, we’ll add an entry to the dependencies
section of our services’ package.json
file, like so:
"dependencies": {
"@computology/left-pad": "latest"
}
We’re also using the special latest
version string, which tells npm
to always install the latest available version, this way we only have to tag, push and redeploy our services for our modifications to take effect.
Conclusion
While our example might have been made up, the usefulness of continuous delivery and Node.js scoped packages is very real. Anything you can do to shorten iterations and feedback cycles will pay back dividends in productivity across your organization.