A couple of weeks ago the IT team in my company talked about having repositories for the packages we make for our PHP applications so we can switch to a more natural use of composer. We left the meeting with ideas but not with a concrete solution nor a promise to research this topic.
Few days ago I needed to make a javascript package, after creating a repository on our gitlab I noticed an option for Packages & Registries
. As it blew my mind that such an option exists I decided to research it a little and use it for this javascript package if possible.
Here is what I learned in the process.
Options
Gitlab offers a few registries you can work with: Composer, Conan, Maven, NPM, NuGet, PyPi. I have only tried out the NPM registry, but others should also be easy to work with.
Publishing an NPM package to registry
This was actually my first time making an NPM package. So I would like to recommend this post Step by Step building and publishing an NPM typescript package to the first timers like me. It was very easy to understand and no steps were missed.
First of all in your package.json
you should scope your project cause Gitlab requires packages to be scoped.
For example:
{
"name": "@scope/example-package-name",
"version": "1.0.0"
}
After we have this setup, if we use a .npmrc
file or npm config set registry
we can tell npm where we want it to publish our package. Looks something like this:
//gitlab.example.com/api/v4/projects/${PROJECT_ID}/packages/npm/:_authToken=${GITLAB_DEPLOY_TOKEN}
If the repository is set to internal or private you need to use a Gitlab deploy token. On how to get one, you can read at Deploy tokens documentation.
After running npm publish
you should be able to see your package in the registry of your repository.
And you should be able to see a version 1.0.0 that says it was pushed manually.
CI/CD
To make our life and the life of our colleagues better we can make good use of the gitlabs CI/CD system here.
We can use .gitlab-ci.yml
configuration that looks like this:
stages:
- build
- test
- publish
build:
stage: build
only:
- tags
cache:
key: build-cache
paths:
- node_modules/
- lib/
- .npmrc
policy: push
script:
- echo "//gitlab.example.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}">.npmrc
- docker run -v $(pwd):/app -v /home:/home -w="/app" -u="$(id -u):$(id -g)" -e HOME node:14 npm install
- docker run -v $(pwd):/app -v /home:/home -w="/app" -u="$(id -u):$(id -g)" -e HOME node:14 npm run build
test:
stage: test
only:
- tags
cache:
key: build-cache
paths:
- node_modules/
- lib/
- .npmrc
policy: pull
script:
- docker run -v $(pwd):/app -v /home:/home -w="/app" -u="$(id -u):$(id -g)" -e HOME node:14 npm run test
lint:
stage: test
only:
- tags
cache:
key: build-cache
paths:
- node_modules/
- lib/
- .npmrc
policy: pull
script:
- docker run -v $(pwd):/app -v /home:/home -w="/app" -u="$(id -u):$(id -g)" -e HOME node:14 npm run lint
publish:
stage: publish
only:
- tags
cache:
key: build-cache
paths:
- node_modules/
- lib/
- .npmrc
policy: pull
script:
- docker run -v $(pwd):/app -v /home:/home -w="/app" -u="$(id -u):$(id -g)" -e HOME node:14 npm version --no-git-tag-version ${CI_COMMIT_TAG}
- docker run -v $(pwd):/app -v /home:/home -w="/app" -u="$(id -u):$(id -g)" -e HOME node:14 npm publish
Notable points:
- In build stage we make a
.npmrc
file that contains the path of the registry made by using the CI environment variables - All the stages run only on tags, a special way to tell the CI/CD system to only activate when you tag the code in your repository
- We build a cache for node_modules, lib and .npmrc as such we limit the number of scripts we need to run after the build step
- Only the build step makes the cache others only use it, it is defined by push/pull policy
- In publish stage we use a
npm version --no-git-tag-version ${CI_COMMIT_TAG}
command.npm version
is a noisy command that tags and commits code if it detects a directory being a git repository so that's why we use--no-git-tag-version
here. As the stage was triggered by us tagging the code, we have the${CI_COMMIT_TAG}
environment variable available to use for package versioning. After that we just publish the package.
Note
I didn't have a gitlab runner that was setup to use docker normally nor did I have node and npm installed on the machine so I had to use docker run
commands like shown. So... not the most elegant way of doing it.
Now the developers don't have to run any scripts locally, just to commit to the repository and tag the code.
If you'd like to support me writing feel free to buy me a cup of coffee.
Top comments (2)
you can add this script to the top of your file that will run before each stage. Then remove - .npmrc in each stage. Also don't call docker. Just call node directly now that it's set as the default.
default:
image: node:latest
before_script:
- npm ci --cache .npm --prefer-offline
- |
{
echo "@${CI_PROJECT_ROOT_NAMESPACE}:registry=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/npm/"
echo "${CI_API_V4_URL#https?}/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=\${CI_JOB_TOKEN}"
} | tee --append .npmrc
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .npm/
Those are good points, but he already mentioned
I didn't have a gitlab runner that was setup to use docker normally nor did I have node and npm installed on the machine so I had to use docker run commands like shown. So... not the most elegant way of doing it.