Publishing npm Packages Using CircleCI 2.0
Continuous Integration and Delivery
This is a guest post written by Armando Canals, co-founder at packagecloud.
This post will cover the necessary steps to implement a continuous deployment pipeline for NodeJS projects using CircleCI 2.0.
We’ll go over setting up a project using the new CircleCI 2.0 configuration, running tests in CircleCI, and deploying packages to the official npm registry when a tagged commit is pushed to a git repository.
npm and npm Packages
Npm is a package manager for the JavaScript programming language, and it’s the default package manager bundled with the Node.js JavaScript run-time environment.
Npm packages are used to distribute JavaScript code conveniently and reliably. The most common case for using Npm packages is to distribute Node.js programs and easily install them on different systems.
Read more about npm packages on the npm docs site or read more about automated npm publishing with CircleCI and packagecloud.
Why publish npm packages?
If you want to share Node.js programs and JavaScript libraries with other developers, business customers, or even Continuous Integration tools (like CircleCI), publishing npm packages to a registry is the easiest way to do so.
In this post, we’ll go over publishing packages to the official (public) npm registry and a private npm registry hosted on packagecloud.io.
Configuring the npm registry with CircleCI 2.0
Add a .circleci/config.yml
to your NodeJS project
In the project root, create a folder called .circleci
, and within that folder create a file called config.yml
.
The following is the full example .circleci/config.yml
file for this blog post:
# Javascript Node CircleCI 2.0 configuration file
#
# Check {{ '/language-javascript' | docs_url }} for more details
#
version: 2
defaults: &defaults
working_directory: ~/repo
docker:
- image: circleci/node:8.9.1
jobs:
test:
<<: *defaults
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: npm install
- run:
name: Run tests
command: npm test
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}
- persist_to_workspace:
root: ~/repo
paths: .
deploy:
<<: *defaults
steps:
- attach_workspace:
at: ~/repo
- run:
name: Authenticate with registry
command: echo "//registry.npmjs.org/:_authToken=$npm_TOKEN" > ~/repo/.npmrc
- run:
name: Publish package
command: npm publish
workflows:
version: 2
test-deploy:
jobs:
- test:
filters:
tags:
only: /^v.*/
- deploy:
requires:
- test
filters:
tags:
only: /^v.*/
branches:
ignore: /.*/
Now, let’s breakdown this configuration to explain the steps involved in each job.
Setting Defaults
The following stanza used in the config.yml
allows us to save some keystrokes by defining a map named defaults
and inserting it in subsequent sections of the configuration file using the YAML merge (<<: *)
key.
defaults: &defaults
working_directory: ~/repo
docker:
- image: circleci/node:8.9.1
These defaults set the working directory where the project code will be checked out, and the version of NodeJS
to be used for the jobs.
Set the $npm_TOKEN
Environment Variable in CircleCI
Use the npm CLI and use the login
command to retrieve the npm authentication token. Run the npm login
command in a terminal and use your npm credentials when prompted.
Once completed, a .npmrc
file will be created in the user directory on the machine where the command was run.
Usually, this is in ~/.npmrc
.
This token is fake - never publish your auth tokens, ever.
Once you’ve retrieved the auth token from npm, create an environment variable with the name $npm_TOKEN
in the project settings on CircleCI.
Alternatively, if you prefer to keep your sensitive environment variables checked into git, but encrypted, you can follow the process outlined at circleci/encrypted-files.
Job Configuration
The jobs section of the config.yml
defines the jobs that’ll run in the workflows, along with the steps for each job.
Test
The first job we’ve defined is the test
job, which retrieves the project code, installs the dependencies to our workspace and runs our tests:
jobs:
test:
<<: *defaults
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}.
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: npm install
- run:
name: Run tests
command: npm test
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}
- persist_to_workspace:
root: ~/repo
paths: .
The command run by npm test
is defined in the scripts
section of the package.json
in the npm package.
This step also restores any dependencies from the cache to save on resources when rebuilding from previous jobs. We can use {{checksum “package.json”}}
as a cache key for CircleCI so that it only installs dependencies when the checksum of the package.json
changes.
Read more about this configuration in the CircleCI docs.
Deploy
The deploy job has several steps that run to authenticate with and publish to, the official npm registry:
jobs:
...
deploy:
<<: *defaults
steps:
- attach_workspace:
at: ~/repo
- run:
name: Authenticate with registry
command: echo "//registry.npmjs.org/:_authToken=$npm_TOKEN" > ~/repo/.npmrc
- run:
name: Publish package
command: npm publish
-
Step 1 - Attach to the workspace we configured
-
Step 2 - Authenticate with the npm registry using the
$npm_TOKEN
set as an environment variable in the CircleCI project settings. -
Step 3 - Run the
npm publish
command from the working directory.
Repo with package
Deploy to a private npm registry
Deploying packages to a private registry is very similar to publishing packages on the official one provided by npm.
By adding an additional step before authenticating with the npm registry, we can specify which registry the npm command will use when deploying.
We’ll use an npm registry on packagecloud as an example:
jobs:
...
deploy:
<<: *defaults
steps:
- attach_workspace:
at: ~/repo
- run:
name: Set registry URL
command: npm set registry https://packagecloud.io/armando/node-test-package/npm/
- run:
name: Authenticate with registry
command: echo "//packagecloud.io/armando/node-test-package/npm/:_authToken=$npm_TOKEN" > ~/repo/.npmrc
- run:
name: Publish package
command: npm publish
-
Step 1 - Attach to the workspace we configured
-
Step 2 - Set the npm registry URL to the private packagecloud registry — this is the fully-qualified URL to a packagecloud npm registry:
https://packagecloud.io/:username/:repo_name/npm/
Where :username
is the user on the packagecloud npm registry and :repo_name
is the name of the npm registry created by the packagecloud user.
-
Step 3 - Authenticate with the npm registry using the
$npm_TOKEN
set as an environment variable in the CircleCI project settings. In this case, a valid packagecloud API Token will grant write-access to the packagecloud npm registry. Check out thenpm login
docs on packagecloud.io for more information. -
Step 4 - Run the
npm publish
command from the working directory.
Using CircleCI workspaces
We set up a workspace in the config.yml
to share data between jobs. Creating a workspace allows us to checkout the project code and share it between the test
and deploy
jobs.
As a step in the test
job, we persist the previous steps completed in the job to the workspace with the following stanza:
- persist_to_workspace:
root: ~/repo
paths: .
Then, in the subsequent deploy
job, we attach to the workspace with the following configuration as the first step in the job:
- attach_workspace:
at: ~/repo
Git Tag Job Execution - Deploy on tagged commit
As part of our continuous delivery pipeline, we want to make sure the deploy
job runs /only/ when a commit has been tagged with a version number and pushed.
workflows:
version: 2
test-deploy:
jobs:
- test:
filters:
tags:
only: /^v.*/
- deploy:
requires:
- test
filters:
tags:
only: /^v.*/
branches:
ignore: /.*/
The workspace configuration above will run the test
job on all branches and all tags and run the deploy
job only when a commit is tagged with a version number and pushed.
The tags
section is using a regex to match tags that begin with a v
, for example, v0.1.0
.
Read more about Git Tag Job Execution here
Conclusion
Automating manual processes in the software development lifecycle can significantly reduce errors, development time and user frustration. Using tools like CircleCI can allow your team to build a robust software distribution pipeline to get software to users quickly and reliably, without adding additional infrastructure.
Start publishing npm packages directly to any public, or private, registry using the powerful CircleCI 2.0 automation tooling.