Simplify Package Versioning with Changesets for GitLab Private Registries

Haley Ward
8 min readApr 26, 2024

Reasoning

When working on setting up our Design System’s React package, I encountered challenges configuring Changesets and publishing to GitLab’s private Package Registry. The lack of comprehensive resources led me through a trial-and-error process. This post aims to provide a detailed guide to streamline this setup for others, saving them from similar frustrations.

Prerequisites

  • A PNPM or Yarn workspace (I opted for pnpm) with a package already setup and producing a build

For your package.json, ensure the following fields are properly configured:

  • Main, Files, and Types (optional): These fields should be set accordingly to define the main entry point, the files to be included in the package, and optionally, the type definitions.
  • Exports: If your package includes multiple exports, such as components and icons, it’s recommended to utilize the exports field to specify these exports.
  • TypesVersions: When each export contains type definitions, it’s essential to include the typesVersions field to ensure compatibility.

Below is an example illustrating how these configurations might appear:

{
"name": "@company/example-react",
"type": "module",
"files": [
"dist"
],
"main": "./dist/index.cjs",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./icons": {
"types": "./dist/icons.d.ts",
"import": "./dist/icons.js",
"require": "./dist/icons.cjs"
}
},
"typesVersions": {
"*": {
"index": [
"./dist/index.d.ts"
],
"icons": [
"./dist/icons.d.ts"
]
}
}
}

Now that we are all setup, let’s get started! 🙌

Changesets

The changesets workflow is designed to help when people are making changes, all the way through to publishing. It lets contributors declare how their changes should be released, then we automate updating package versions, and changelogs, and publishing new versions of packages based on the provided information.

  1. Install the changesets/cli package to the workspace root:
pnpm install @changesets/cli -w

2. Initialize Changesets by running the following:

pnpm changeset init

This command sets up the .changeset folder. It generates a README and a config file. The config file includes the default options. You can see all available config options here.

{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}

NOTE: access is kept as restricted since this is going to be published as a private package.

The Changesets package is designed to work seamlessly with GitHub Actions, so we need to integrate Changesets with GitLab using the changesets-gitlab package.

  1. Install changesets-gitlab package to the workspace root by running the following:
pnpm install changesets-gitlab -w

GitLab CI: Comment Job

Changesets comment command automates commenting on GitLab Merge Requests by identifying related changesets and generating informative comments. This automation streamlines collaboration, improves visibility into proposed changes, and enhances versioning and release management workflows.

  1. In our gitlab.ci.yml file, let’s create the new comment job. It will look something like this:
stages: 
- comment

changesets_comment:
stage: comment
script:
- pnpm install
- pnpm changesets-gitlab comment
variables:
- GITLAB_TOKEN: $YOUR_TOKEN_HERE
rules:
if: $IS_MERGE_REQUEST

Let’s dig into what each line is doing

  1. stages: comment : we are creating a new comment stage that will then be used in our new job
  2. changesets_comment : this is the name of our new job
  3. stage: comment : we are defining which stage in the pipeline that this job should be ran in
  4. script: pnpm install : we are installing all of our dependencies so that when we run our next script, changesets-gitlab is available to use
  5. script: pnpm changesets-gitlab comment : this runs the command that will detect if there are any changesets and then comment on the current Merge Request
  6. variables: GITLAB_TOKEN: $YOUR_TOKEN_HERE : here we are setting a GitLab token that is required for authentication or authorization purposes. NOTE: CI_JOB_TOKEN may not work. If you see a forbidden error message then that is most likely why. I had to create an environment variable that had developer permissions.
  7. rules: if: $IS_MERGE_REQUEST : this rule ensures that the job runs only on Merge Requests

TADA🎉 🎉 🎉! Now after we’ve committed and pushed our changes, we should see a new comment from our Changesets Bot.

Why aren’t any changesets found? Well we haven’t generated a new changeset yet. So let’s go ahead and create one.

Generating a Changeset

For this step, ensure that you have a change made in your package that you’d like to version.

  1. In the workspace root, run the following command to generate a changeset:
pnpm changeset

This command will then populate a few prompts. Changesets follows semver, so it will ask if you’d like this to be a patch, minor, or major version. After selecting which version you’d like to bump this package, it then asks for a summary of this change. This will be added to the changelog and posted in the description for the “Version Packages” Merge Request — this Merge Request is automatically generated for you by Changesets. I’ll go into this more in a moment. After the summary is written, you will then be asked to verify these changes. Press the enter key to accept the changes.

As we can see, a new file was generated in the .changeset directory, which lists which package this changeset is for, if it’s a patch, minor, or major change, and the summary you filled out above.

Now if we commit and push these changes, we should see our Changeset Bot’s comment updated with the detected changes. Note here that I did a patch in the step above, and in the comment below Patch is listed as the type.

GitLab CI: Publish Job

Before we get started, let’s ensure we have a few package.json configuration settings handled.

Tell our package where its registry is located:

"publishConfig": {
"registry": "https://gitlab.com/api/v4/project/{YOUR_PACKAGE_REGISTRY_PROJECT_ID}/packages/npm/"
}

Now that we have that set and the Changesets detected, we can finally publish our package. In our gitlab-ci.yml let’s create the publish job.

stages: 
- publish

publish:
stage: publish
script:
- pnpm install
- |
cat <<EOF > "$HOME/.npmrc"
//${CI_SERVER_HOST}/api/v4/projects/${YOUR_PACKAGE_REGISTRY_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}
EOF
- pnpm build
- pnpm changesets-gitlab
variables:
GITLAB_TOKEN: $YOUR_TOKEN_HERE
NPM_TOKEN: $CI_JOB_TOKEN
INPUT_PUBLISH: pnpm changeset publish
rules:
- if: $IS_MAIN_BRANCH

Let’s dig into what each line is doing

  1. stages: publish : we are creating a new publish stage that will then be used in our new job
  2. publish : this is the name of our new job
  3. stage: publish : we are defining which stage in the pipeline that this job should be ran in
  4. script: pnpm install : we are installing all of our dependencies so that when we run changesets-gitlab it is available to use
  5. script: | ... : we are creating or updating the .npmrc file that is in the home directory. In this file, we are adding the line in between cat and EOF which adds authentication credentials for accessing the package registry associated with GitLab. We have to use the CI_JOB_TOKEN variable for authentication.
  6. script: pnpm build : we need to ensure that we have the latest build for our new changes.
  7. script: pnpm changesets-gitlab : this will trigger an action, such as versioning or updating changelogs based on identified changesets.
  8. variables: GITLAB_TOKEN: $YOUR_TOKEN_HERE : similar to the comment job, we are setting a GitLab token that is required for authentication or authorization purposes. NOTE: CI_JOB_TOKEN may not work. If you see a forbidden error message then that is most likely why. I had to create an environment variable that had developer permissions.
  9. variables: NPM_TOKEN: $CI_JOB_TOKEN : this sets an npm token used for authentication with the npm registry. NOTE: It should be the same token that you specify in the .npmrc .
  10. variables: INPUT_PUBLISH: pnpm changeset publish : this is the actual command to publish our package to the registry. It will check to see if the package is published and if there is a version difference.
  11. rules: if: $IS_MAIN_BRANCH : we check to make sure we are only running this job on main. We want to ensure that we are only publishing our package with production-ready code.

Versioning

Alright, so let’s commit these changes. We won’t see any changes made to the Merge Request. The real magic happens once we merge it in. After merged, we can watch the main pipeline to see the new publish job run.

Changesets will detect a new changeset and generate a new Merge Request which will bump the version to the correct semantic version we set and then remove the changesets and create a changelog for that version. If you have multiple packages in your workspace, Changesets will bump each package’s version in the same Merge Request.

In the “Version Packages” Merge Request description, we will see: “This MR was opened by the changesets-gitlab GitLab CI script”. But the good stuff is below that under Releases. Here it will list out all of our packages that are being versioned. It will then list the version changes and commits + release note for each change. This will allow you to confirm that the version is correct and all of your expected changes are included.

Publishing to Package Registry

Once the “Version Packages” Merge Request has been merged, we can follow the main pipeline again and see it finally publish our package.

Now it has been published to our private GitLab registry and a new tag for 0.0.1 was created. Let’s double check that our package was published. Head over to GitLab and under the project that you specified, go to the “Deploy” tab and click on “Package Registry”. We should now see our package listed with the correct tag. Like so:

🎉🎉🎉

Installing Your Package in Another Repository

Now any repository in your GitLab organization can use this package, but it won’t work by simply installing the package like you would any other npm package because it is on your GitLab Private Registry. Let’s go over how we can access our newly published package. This should only need to be done once for setup.

  1. Authenticate in order to download an npm package:
npm config set -- '//gitlab.com/api/v4/projects/{YOUR_PACKAGE_REGISTRY_PROJECT_ID}/packages/npm/:_authToken "<TOKEN>"

Ensure that the YOUR_PACKAGE_REGISTRY_PROJECT_IDis the same id that you used for the .npmrc authentication in the publish job. The token should be a personal access token.

2. Create an .npmrc in the repository where you want to install the new package. Here we will specify a registry for where the new package lives.

@company:registry:https://gitlab.com/api/v4/projects/{YOUR_PACKAGE_REGISTRY_PROJECT_ID}/packages/npm

3. Install the new package

pnpm install @company/example-react

You may run into an error since you are adding a new registry about reinstalling dependencies. Run pnpm install and then rerun the command to install your package. Voila! You can now use your newly published package!

Conclusion

To someone who reads all the way to here, I hope this was super helpful and you were able to successfully publish your private package to GitLab using Changesets.

This is my first time writing a tutorial blog so I hope it was easy to follow along with. If you find an issue or if there are areas for improvement with this work, please leave a comment. Feedback is always welcome here. ❤️

--

--

Responses (1)