Sakalim

Sakalim

About Me

January 5, 2023

How to deploy automatically to NPM and Github packages from NRWL NX workspace

6 min read

    CLI

Technologies

GitHub Packages
GitHub Actions
NPM
nrwl/nx v15
node v16
@theunderscorer/nx-semantic-release v1

In projects with a streamlined deployment process, I tend to use TheUnderScorer/nx-semantic-release: Package for automated releases for nx built on semantic-release. The nx-semantic-release library is powerful as it:

  • leverages the NX workspace graph capabilities to determine which application/library was modified.
  • has excellent integration with the semantic release.
  • let you choose between Github actions and CLI.
  • Offer effective configuration variations (environment variables, config file, nx project.json, CLI arguments).

Extend your project with automatic deployment to the NPM registry

First, follow the installation from the official library page https://github.com/TheUnderScorer/nx-semantic-release.

Review the created .nxreleaserc.json file. You will probably need to change the branch from master to main in the file.

In file packages/{projectName}/project.json, where you previously added the executor, add some options:

"semantic-release": {  
  "executor": "@theunderscorer/nx-semantic-release:semantic-release",  
  "options": {  
    "buildTarget": "${PROJECT_NAME}:build",  
    "outputPath": "dist/packages/${PROJECT_NAME}"  
  }  
}

A few things to note:

  1. The properties buildTarget and outputPath are required for nx-semantic-release to deploy to NPM or GitHub Actions.
  2. Replace {PROJECT_NAME} with your actual project name.

Ensure you have the following settings in the file packages/{projectName}/package.json.

{  
  "name": "obsidian-album",  
  "version": "1.0.0",  
  "type": "module",  
  "bin": "./src/cli.js",  
  "private": false,  
  "author": {  
    "email": "eran@sakalim.com",  
    "name": "Eran Sakal"  
  },  
  "engines": {  
    "node": ">=18"  
  },
  "publishConfig": {
	"access": "public"
  },
  "repository": {  
    "type": "git",  
    "url": "https://github.com/esakal/obsidian-album"  
  }  
}

A few things to note:

  • The properties private, author, and repository are mandatory.
  • Make sure the version's initial value is accurate. If it is currently set to 0.0.0, change it to 1.0.0.
  • The property access is needed if you publish a public library.
  • If you publish to GitHub packages, you should read section Deploy to GitHub packages registry using GitHub Actions.

Create file .github/workflows/release.yml :

name: Release to NPM  
  
on:  
  push:  
    branches:  
      - 'main'  
  
jobs:  
  release:  
    runs-on: ubuntu-latest  
    steps:  
      - name: Checkout  
        uses: actions/checkout@v3  
      - name: Setup Node.js  
        uses: actions/setup-node@v3  
        with:  
          node-version: 18          
      - run: npm ci  
      - run: npx nx run-many --target=semantic-release --all --parallel=1
        env:  
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}  
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

A few things to note:

  • If you deploy multiple packages with change log creation, you should adjust the release.yml file. See section Deploy multiple packages with change log feature enabled.
  • The NODE_AUTH_TOKEN environment variable is used to publish to NPM. You should create an NPM token and assign it to this variable.
  • The GITHUB_TOKEN environment variable is used to let TheUnderScorer/nx-semantic-release: Package for automated releases for nx built on semantic-release push change logs to Github while deploying.
  • The NODE_AUTH_TOKEN and NPM_TOKEN are used to allow deployment to the NPM registry
  • Passing --parallel=1 is essential if you left the default behavior to commit artifacts during deployments like the CHANGELOG.md file and the new version into package.json. It will fail without it. If you decide to create the CHANGELOG.md files, consider doing that only for official versions (not feature/pre-release versions).

Deploy to GitHub packages registry using GitHub Actions

When you need private libraries, using the GitHub packages registry instead of the NPM registry is very handy. Deploying the package to the GitHub registry requires some adjustments to the NPM configuration. Otherwise, it will try to deploy it to the NPM registry.

Info

This article shows how to deploy GitHub packages. To learn how to install the library from GitHub packages, read the article How to use private GitHub packages in your repository and with Github Actions.

In .github/workflows/release.yml

  1. Add registry-url and scope arguments
- name: Setup Node.js  
  uses: actions/setup-node@v3  
  with:  
    registry-url: 'https://npm.pkg.github.com'  
    scope: '@esakal'
  1. Use the GitHub token when running
- run: npx nx run-many --target=semantic-release --all
  env:  
    NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}  
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  

Deploy to GitHub packages from CLI

If you deploy without Github actions, you can adjust the file packages/{projectName}/package.json, add:

"publishConfig": {  
  "registry": "https://npm.pkg.github.com"  
},

OR you can use file .npmrc and bind the scope to GitHub Packages.

@esakal:registry=https://npm.pkg.github.com

Support feature/pre-release branches

Friendly warning

Usually, when working with the semantic release approach, people squash Pull requests instead of merging them. Squashing pull requests instead of merging them is a good approach in general, as it lets you work on a pull request peacefully and care about the commit message only when ready to squash it into the main branch.

When dealing with feature/pre-release versions, you should always merge (and not squash) them to avoid undesired conflicts that are extremely hard to resolve. And anyhow, it is the right thing to do regardless.

Having the ability to create feature/pre-release versions is super helpful. Not many people use it as, mostly due to dev-ops complexities. To learn more read What Is a Feature Branch + How they Improve the Dev Process | Bunnyshell |.

To support it in our workflow do the following adjustments.

In file .github/workflows/release.yml, make sure it is also being triggered when changing the hotfix branch or release/something branches

on:  
  push:  
    branches:  
      - main
      - hotfix
      - 'release/*'

In the file .nxreleaserc.json, add:

branches: [  
  {  
    name: 'release/*',  
    prerelease: '${name.replace(/^release\\//g, "")}'  
  },  
  'main',  
  {  
    name: 'hotfix',  
    pre-release: true  
  },  
]

A few things to note:

  1. The branches configuration is used under the hood by the semantic release libraries; it instructs semantic release to create pre-releases for the branch hotfix and for any feature branch whose name starts with release/. So a branch named release/studio will create a pre-release version like cli-x1.2.3-studio.0.