GitLab offers a private NPM registry for each project with the GitLab Packages feature.

We will see in this article how to use it to publish modules then how to automate the releases and the changelog generation on a project.

Requirements

You will need an existing NPM module project.

First step : package.json update

Add a main entry in the package.json to declare the module entry point.

"main": "src/index.js"

Then, add a publishConfig object.

"publishConfig": {
  "access": "public"
}

⚠️ Important : You can also need to update the project name if the scope (GitLab group) is missing.

Why ? GitLab Packages use the project GitLab group as NPM scope.

Example :

The project module ziggornif/awesome-project project will be declared as @ziggornif/awesome-package

Your project is now well configured to be published in the GitLab NPM project registry.

Configure CI

Now, we can set up a CI pipeline which will publish the module versions.

In this example, we will create only one release stage that will be triggered on the main branch.

Create a .gitlab-ci.yml file in the project.

default:
  image: node:16
  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/

workflow:
  rules:
    - if: $CI_COMMIT_BRANCH

stages:
  - release

publish:
  stage: release
  script:
    - npm publish
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Explanations :

  • The default part install node modules from cache (if exists) and setup .npmrc file to publish module
  • The publish job declared on the release stage run the publish module action
  • The pipeline is only triggered on main branch commits.

Let’s publish a version !

With this configuration, every commit on the main branch will trigger a pipeline like the following :

This picture is a screenshot of the Gitlab CI pipeline view. It shows that the publish job in the release stage ending successfully.

We can see that the publish job works well. The version is published in the GitLab project package registry.

This second picture of the publish job show this time the job logs. In the logs, we can see the npm publish command that prepare the module archive then publish it to the gitlab packages registry. The package is published with version : @ziggornif/npm-package-article@1.0.0.

Retrieve published versions in GitLab

Now, go to the package registry page (left side bar).

Packages & Registries Gitlab menu. Package Registry menu choice

You should now have a new entry in the package page.

This picture is a screenshot of the Gitlab Package Registry page. It shows the @ziggornif/npm-package-article that we have published with the CI in 1.0.0 version.

If you click on the version entry, you will retrieve the build information and the registry setup to use the module in your projects.

The package informations view with name, version, creation date, commit SHA and pipeline ID. This view also give example to install the package in a project : npm i @ziggornif/npm-package-article and to add the gitlab registry in a JS project : echo @ziggornif:registry=https://gitlab.com/api/v4/packages/npm/ >> .npmrc

Releases and Changelog automation with semantic-release

Our project can now publish its versions to the GitLab package registry, but we still need to set the version by hand.

Let’s automate this with the semantic-release module.

Reminder on conventional commits and semantic versioning

The semantic-release module automates the package releases and apply semantic versioning based on conventional commits.

The semantic versioning (SemVer) rules permit to define the next software version.

  • A fix will be a patch version 0.0.x
  • A feature will be a minor version 0.x.0
  • A breaking change will be a major version x.0.0

Conventional commits specification permit to associate a commit type to a semantic versioning rule.

  • fix: commit will produce a patch version
  • feat: commit will produce a minor version
  • BREAKING CHANGE: commit will produce a major version

Install semantic-release and dependencies

Run the following command to add semantic-release dependencies :

npm install semantic-release @semantic-release/git @semantic-release/gitlab \
@semantic-release/npm @semantic-release/changelog --save-dev

Theses dependencies are needed to configure semantic-release with Gitlab and generate the changelog file.

Configuration

Create a .releaserc in the project with the following content :

{
  "branches": ["main"],
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    [
      "@semantic-release/changelog",
      {
        "changelogFile": "CHANGELOG.md"
      }
    ],
    "@semantic-release/gitlab",
    "@semantic-release/npm",
    [
      "@semantic-release/git",
      {
        "assets": ["package.json", "CHANGELOG.md"],
        "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
      }
    ]
  ]
}

Update package.json

In the package.json file, add a semantic-release script :

"scripts": {
  "semantic-release": "semantic-release"
}

Update CI Pipeline

In the .gitlab-ci.yml add a NPM_TOKEN variable.

variables:
  NPM_TOKEN: ${CI_JOB_TOKEN}

Then, update the publish job command.

script:
-    - npm publish
+    - npm run semantic-release

Setup CI/CD variables

Create a personal access token with API scope from your profile page (see GitLab documentation : https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html).

Go to Settings -> CI/CD pages to define a private variable.

The CI/CD variables view. This screen list all the custom variables availables in CI jobs. Variables are displayed in a table and there is a button “Add variable” to create a new one. Variables can be protected (only available in jobs running on protected branches) and masked (value masked in job logs).

In the Variables section, create a variable named GITLAB_TOKEN with the previous generated token.

The CI/CD add variable form. This form allows user to setup a new CI/CD variable. It contains key input which is the variable name (here GITLAB_TOKEN), value input which is the variable value (here the personal access token value), type dropdown (here variable), environment scope dropdown (here All) and protect and mask flags checkbox as we mention previously. At the bottom of the form, two buttons to cancel and add the variable.
We are back on the CI/CD variables view. This time we can the GITLAB_TOKEN created variable in the variables list which contains the personal token. We can also see in the list that the variable is protected and masked (in job logs).

This variable will be used by the semantic-release module.

Release time !

Add a feat commit on the project and let the magic happens.

We can the that the semantic-release module pushed a release commit and a new tag.

pipeline-view

The generated tag contains the version changelog.

generated-tag

And the CHANGELOG.md file has also been updated with the version content.

changelog-file

Thats it !!! πŸ“¦ πŸš€

Project example

You can retrieve and fork the complete project here : https://gitlab.com/ziggornif/awesome-package

Useful resources

GitLab documentation :

Semantic release project : https://github.com/semantic-release/semantic-release

Changelog module : https://github.com/semantic-release/changelog

Semantic versioning : https://semver.org/

Conventional commits : https://www.conventionalcommits.org/en/v1.0.0/