Continuously Deploying to GitLab's NPM Package Registry
Tags: devos, gitlab, javascript, npm
Published: 2022-07-05 13:29:20

---
title: Continuously Deploying to GitLab's NPM Package Registry
published: true
date: 2022-06-04 00:00:00 UTC
tags: ["devos", "gitlab", "javascript", "npm"]
canonical_url: https://webbureaucrat.gitlab.io/articles/continuously-deploying-to-gitlabs-npm-package-registry/
---

In a [previous article](https://webbureaucrat.gitlab.io/articles/better-continuous-deployment-with-gitlab-ci-cd), we explored how to continuously deploy to the [npmjs.com](http://npmjs.com/) package registry. This is all well and good, but an advantage of CI/CD is that we can easily deploy wherever we want. This article will explore how to deploy an npm package to both [npmjs.com](http://npmjs.com/) and GitLab's own package registry, including how to change package names when necessary.

### Starting point: ordinary CI/CD for npm

The earlier article built out a complete _.gitlab.yml_ file for an npm package, and it leverages environment variables in a way that makes it very transferrable between projects. I will be adding a stage to this pipeline, so I recommend familiarizing yourself with this pipeline as a starting point before proceeding. The comments in the first stage provide a kind of legend for the file in case you're not familiar with _.gitlab.yml_ attributes.

```yaml
image: node:latest

compile: # arbitrary name to identify the script
  stage: build # indicates its chronological order in the pipeline
  script: 
    - npm ci # the recommended best practice for CI/CD (as opposed to npm i)
    - npm run build 
  only: 
    - dev # only run this script for the dev branch
  
test:
  stage: test
  script:
    - npm ci
    - npm run build
    - npm run test
  only:
    - dev

merge:
  only:
    - dev
  script:
    - git remote set-url origin https://merge-token:${MERGE_TOKEN}@gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}.git
    - git pull origin main
    - git checkout main
    - git merge origin/dev
    - git push origin main
  stage: deploy
    
deploy:
  only:
    - main
  stage: deploy
  script:
    - echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> .npmrc
    - npm publish
```

If you're still confused, refer to the[previous article](https://webbureaucrat.gitlab.io/articles/better-continuous-deployment-with-gitlab-ci-cd/), which builds this file step by step (or, as always,[open an issue](https://gitlab.com/groups/webbureaucrat/-/issues)).

### Scaffolding our new deployment step

Let's start by creating a new deployment script which runs on the main branch in the deployment stage.

```yaml
gitlab-deploy:
  only:
    - main
  script:
    - echo "Hello, world."
  stage: deploy
```

So now we've defined the circumstances under which we want to run.

### Managing your package namespace and name

I would expect that you'd want to publish your package under the same name on GitLab as [npmjs.com](http://npmjs.com/), but the GitLab package registry requires that all packages be namespaced by their repository namespace.

So we need to swap out our old package name with our new namespaced package name. For me, since I am working in ReScript and need to rename in _bsconfig.json_ as well as the package files, I've found the easiest way to reconfigure my namespace is through the npm package `file-find-replace-cli`.

(You could also use `sed` if you're smart enough, but I've decided my life is too short for that.)

`file-find-replace-cli` uses a json file to define its commands. This can be predefined and checked into source control, but it's more fun to `echo` it out from environment variables (which will make our _.gitlab.yml_ more reusable).

```yaml
gitlab-deploy:
  only:
    - main
  script:
    - npm i -g file-find-replace-cli
    - echo "{ \"find\":\"$CI_PROJECT_NAME\"," >> replace.json
    - echo "\"replace\":\"@$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME\" }" >> replace.json
  stage: deploy
```

With the package installed and the _replace.json_ file in place, you can run the `find-replace` command against your _package.json_ and _package-lock.json_files using `package*.json` (and _bsconfig.json_ if you, like me, are using ReScript).

```yaml
gitlab-deploy:
  only:
    - main
  script:
    - npm i -g file-find-replace-cli
    - echo "{ \"find\":\"$CI_PROJECT_NAME\"," >> replace.json
    - echo "\"replace\":\"@$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME\" }" >> replace.json
    - find-replace replace.json -f 'bsconfig.json' -f 'package*.json'
    - npm config set @${CI_PROJECT_NAMESPACE}:registry https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/
    - npm config set -- '//gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken' "${CI_JOB_TOKEN}"
    - npm publish
  stage: deploy
```

Next, set the package registry to GitLab for the namespace you just changed to, and authenticate with the built-in `CI_JOB_TOKEN`, and, finally, publish.

```yaml
gitlab-deploy:
  only:
    - main
  script:
    - npm i -g file-find-replace-cli
    - echo "{ \"find\":\"$CI_PROJECT_NAME\"," >> replace.json
    - echo "\"replace\":\"w$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME\" }" >> replace.json
    - find-replace replace.json -f 'bsconfig.json' -f 'package*.json'
    - npm config set @${CI_PROJECT_NAMESPACE}:registry https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/
    - npm config set -- '//gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken' "${CI_JOB_TOKEN}"
    - npm publish
  stage: deploy
```

### Publish or perish

I hope this has been useful to developers trying to deploy to GitLab's registry or deploy to multiple registries. The default [npmjs.org](http://npmjs.org/) registry is an invaluable developer resource, but it's always a good idea to have multiple baskets for one's proverbial eggs. (More on this in a future article.)