Auto publish npm package with Github Actions
Tags: github, npm, devops, javascript
Published: 2020-12-26 06:13:57

Inspired by https://michaelzanggl.com/articles/github-actions-cd-setup/

There are a couple of ways we can set up publishing to NPM: 1. When pushing to a certain branch
2. When creating a tag
3. When creating a commit in a specific pattern
4. When creating a release on GitHub directly.

This is what you need to know about my project: 
1. The code for the latest version is on the branch latest. 
2. The code for version 1 is on the branch v1-latest. 

The reason for these branch names will become apparent later.

### Create the YML file

```yml
name: NPM publish CD workflow

on:
  release:
    types: [published]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
        with:
          ref: ${{ github.event.release.target_commitish }}
      # install Node.js
      - name: Use Node.js 12
        uses: actions/setup-node@v1
        with:
          node-version: 12
      - run: yarn install
      # set up git since we will later push to the repo
      - run: git config --global user.name "GitHub CD bot"
      - run: git config --global user.email "savchenkoalexander91@gmail.com"
      # upgrade npm version in package.json to the tag used in the release.
      - run: npm version ${{ github.event.release.tag_name }}
      # build the project
      - run: npm run build
      # run tests just in case
      - run: npm test
      - run: npm config set '//registry.npmjs.org/:_authToken' "${{ secrets.NPM_TOKEN }}"
      # publish to NPM -> there is one caveat, continue reading for the fix
      - run: npm publish --tag ${{ github.event.release.target_commitish }}
        env:
          # Use a token to publish to NPM. See below for how to set it up
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
      # push the version changes to GitHub
      - run: git push
        env:
          # The secret is passed automatically. Nothing to configure.
          github-token: ${{ secrets.GITHUB_TOKEN }}
```
Go to Github -> your repository -> actions -> set up a workflow yourself

### Get a npm token

Go to npm, in the settings go to "Auth Tokens", and click the button "Create New Token".

### Add the npm token to "secrets"

Copy the token and head over to the project settings of your GitHub repository. Go to "Secrets" and click "New Secret". Give it the name "NPM_TOKEN" and for the value, paste the token inside.

### Coveat

You see, when you do `npm install <package>` it will actually do `npm install<package>@latest` behind the scenes. And when you do `npm publish` it will actually do `npm publish --tag latest`

"latest" is a reserved tag for NPM. However, even though the project is already on version 2, publishing a fix for version 1 will make it the "latest" version. This means when somebody installs the package afterward, he will get version 1 instead of version 2. That's of course not what we want.

So to fix this, we have to specify a different tag when publishing. 

So we have two options here:
1. `npm publish --tag ${{ github.event.release.target_commitish }}` (my choice)
2. Add a default publish tag to the v1 branch package.json:
```json
"publishConfig": {
  "tag": "v1-latest"
}
```

The reason I chose v1-latest over just v1 is that npm tags must not follow semver. Otherwise, NPM would not be able to distinguish a tag from a specific published version.