Continuously Deploying to GitLab's NPM Package Registry
devops gitlab javascript npm rescriptIn a previous article, we explored how to continuously deploy to the 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 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.
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, which builds this file step by step (or, as always, open an issue).
Scaffolding our new deployment step
Let's start by creating a new deployment script which runs on the main branch in the deployment stage.
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, 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).
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).
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.
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 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.)
I write to learn, so I welcome your constructive criticism. Report issues on GitLab.