How to Automate CI/CD in a Lerna Monorepo

by Thilo Haas

March 2021

You might have read our recent blog post about how we built a collection of React Micro Frontend Components using a monorepo: React Micro Frontend Components in a Monorepo

Wanna know more about the technical setup and how we integrated and automated the release process?

In this post, you’ll learn how to use Lerna to manage the Monorepo and how to integrate it in a Continuous Integration and Continuous Delivery pipeline using Gitlab CI.

Pipelines

We use the Gitlab Flow branching model with Tags to mark releases. Every single feature or fix needs to be developed in a separate branch. Once ready to review, a pull request is opened to ensure the four eye principle is met and to keep the master branch stable.

This is where the first pipeline starts. Every code change in feature or fix branch triggers the unit and integration tests to be run and linting rules to be checked.

After merging and successful test pipeline runs of the master branch, the semantic release can be triggered by a manual or automated CI job. For that we use the lerna version command that does two things:

  1. It generates a new git tag for every package listing its changes and
  2. It publishes the new package versions to NPM

Lerna

For our monorepo setup we use Lerna. It provides all the necessary processes and utilities needed to manage the mono repository. We tightly integrated it in our gitlab CI release pipeline so that new releases are created automatically and consistently.

Per default, Gitlab CI runs the CI jobs in a detached HEAD state of the repository tree. This is why Lerna cannot correctly determine the current branch and git history tree and therefore fails to publish a new version of the packages.

The problem is also described in the following Github issue on the Lerna project.

We solve this issue by first checking out the current branch of the pipeline:

# clone the repository
git clone "https://${GL_USER}:${GL_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" dist &> /dev/null
cd dist
git checkout ${CI_COMMIT_REF_NAME}
git pull origin ${CI_COMMIT_REF_NAME}
npm install
# create new git version tags
lerna version --yes
# publish the new git versions to npm
lerna publish from-git --yes

Conventional Commits and Semantic Versioning

We strictly adhere to semantic versioning and use conventional commits.

Meaning every commit merged into master must be in the form of fix: correct minor typos in code. This guarantees that the semantic-release process of lerna can distinguish if it should create a new major, minor or patch release of the corresponding component.

This is our lerna.json file with the configuration to respect conventional commits and semantic versioning:

json
{
"packages" : ["packages/*"],
"version": "independent",
"command": {
"version": {
"createRelease": "gitlab",
"conventionalCommits": true,
"allowBranch": ["master"],
"message": "chore(release): publish"
}
}
}

How did you automate your application development and deployment process? We would love to talk about your experiences! Let’s get in touch!

Previous post
Back to overview
Next post