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.

npm Auth token 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.

CircleCI Environment Variable Section

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 the npm 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

Completed CircleCI workflow image

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.