Open sourcing node-publisher

By Attila Večerek

Imagine a hypothetical scenario where we have a Node.js package (A) with a dependency graph depicted in the image below:

The dependency graph of our hypothetical package “A”

We’ve just fixed an annoying bug in dependency C and our task is to propagate the fix up until package A. So, we do the following things:

  1. Release a new version of C and update package B to depend on the new version of C.
  2. Release a new version of B, and similarly update package A.
  3. Release a new version of A.

Sounds easy, doesn’t it?

As the first step, we’ll release a new version of C based on a very simple 10-step process documented in its README. We install the new version in package B, test our changes and repeat the release process. We’ll do the same with package A, and… the bug is still there?!

After an hour of confused debugging, it turns out we left out one (critical) step from the “simple” release process and the new version of C did not contain the fix. Thus, we need to go back to step 1 and go through the whole process all over again. This time, we messed up because of our lack of attention. However, in general, we can fail due to different reasons: outdated documentation, mismatched Node or Yarn versions, etc.

The hypothetical scenario described above is not as hypothetical as one might think. Releasing packages can be quite a mundane task, thus more susceptible to human error; one that is difficult to spot. At Zendesk, we found ourselves repeating the same mistakes over and over again:

  • forgetting to run the build,
  • releasing from the wrong branch,
  • releasing without files being checked in,
  • forgetting to run linting and tests,
  • testing the wrong version, etc.

And that’s why we’ve built node-publisher, a zero-configuration release automation tool for node packages inspired by create-react-app. A tool that converts simple 10-step release processes into 1-command instructions.

Node Publisher: shipping packages made fast and effortless.

When we were designing node-publisher, we looked into the release documentation of several of our own node packages. We found that the release mainly consists of 5 steps, and node-publisher has been built with those steps in mind:

  1. Preparation: several checks regarding the working tree and the current Node version.
  2. Testing: running linters, tests, etc.
  3. Building (optional): transpiling code with Babel.
  4. Publishing
  5. Post-publishing: pushing to remote, generating the changelog, etc.

The tool is built to detect the correct node version to check against, what dependency management tool the project uses (supports npm, yarn, and lerna) and whether a build step is needed. The changelog is generated using the offline-github-changelog tool written by Sune Simonsen.

Node-publisher works with several assumptions about the project such as the use of a specific git workflow, the existence of a .nvmrc file, scripts’ namings in package.json, etc. To fully customize the release process, node-publisher can be “ejected”, which then produces a YAML file in the root of your project under the name .release.yml. Assuming your project uses yarn, the produced config file will look like this one.

The release configuration file is inspired by Travis CI. Under each key (except rollback) you may define a set of instructions to be run in your terminal. Steps that are not needed may be deleted. The tool will only run the steps and commands that are present in the configuration file. When the rollback option is set to true, node-publisher will create a rollback point after the “prepare” step. In case of failure, it will then reset the working tree to that point in time.

With node-publisher, the only command you will ever need to release a new version of your package is the one below.

yarn release major

This tool has been adopted by several of our own projects, and now that it’s open we’d be happy to see it being used in other projects, too. Feel free to open a GitHub issue in case of questions or a PR if you wish to contribute :-)