# Automated release process for Lerna monorepo

The process of publishing (releasing) a new version of software is one of the most important steps in a development lifecycle. In this article, we will take a look at how we can automate this in a monorepository managed with lerna.

- URL: https://sudolabs.com/insights/automated-release-process-for-lerna-monorepo
- Published: 2021-02-08
- Updated: 2024-01-16
- Author: Filip Jenik
- Categories: Tech
- Language: English

The process of publishing (releasing) a new version of software is _one of the most important steps_ in a development lifecycle. In this article, we will take a look at how we can **automate** this in a monorepository managed with [lerna](https://lerna.js.org/).

## Motivation

The most important things in the release process are _simplicity_, _consistency_, and _clarity_. A release process should be _straightforward_, so everybody on the team can do it **easily by following a clear set of instructions**. During the release process, a lot of things can go wrong - you forget to set a new version, modify the version in any of the `package.json` files, or even forget to create a new tag or merge hotfixes from the `master` branch. Therefore, **it's good to clearly define this process and automatize it as much as possible**.

## Goals

The main goal of this article is to **define the release process** for monorepo using Lerna. It consists of the _automatic creation of tags, incrementing the version, and changelog generation with the chronological arrangement of changes_.

## Project structure

For this project, we will be using a monorepo with two packages/modules: `api` and `frontend`. In the root folder, we have packages, root `package.json`, and Lerna config.

```
+-- api
| +-- package.json
| +-- ...
+-- frontend
| +-- package.json
| +-- ...
+-- CHANGELOG.md
+-- lerna.json
+-- package.json
+-- ...
```

In this article, we are fixed to monorepo projects managed by `lerna`. That means that you should be able to use this process with any project using this structure (maybe with some little changes in configs). **_You can find valuable information in this article even if you are not using this kind of setup_**.

## Setup

In our release process solution, we are using a _combination_ of `GitHub`, `lerna` and `lerna-changelog`.

### Lerna

Lerna is a library that provides tooling to manage multi-package structure inside a single repository (sometimes called monorepos). Our main usage of `lerna` is for _automatic versioning and tag generation_. We are using `lerna` with the combination of `lerna-changelog` package which is used to _generate a changelog_ based on the labels attached to PRs merged into the origin.

In the root of the project run `npm i -s -d lerna` (we are currently using `3.22.1`).
Create `lerna` configuration file (`lerna.json`).

```json
{
	"packages": [
		"./*"
	],
	"version": "{{your_current_repository_version}}"
}
```

- packages - Array of globs to use as package locations (locations of your **not** root `package.json` files)
- version - the current version of the project (from the root `package.json`)
  More `lerna` config options can be found [here](https://github.com/lerna/lerna#lernajson).

### Lerna-changelog

In the root of the project run `npm i -s -d lerna-changelog` (we are currently using `1.0.1`)
In the root `package.json` file we need to add config for `lerna-changelog`. You can map PR labels from Github to more meaningful titles here.

```json
"changelog": {
	"labels": {
		"Type: Feature": "Features",
		"Type: Bug": "Bug fixes",
		"Type: Enhancement": "Enhancements"
	},
},
```

More options can be found in [lerna-changelog doc](https://github.com/lerna/lerna-changelog#configuration).

For changelog generation, you'll need a [personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token#using-a-token-on-the-command-line) for the GitHub API with the `repo` scope for private repositories or just `public_repo` scope for public repositories.

You can set the environment variable for the GitHub authentication by running this command:

```shell
export GITHUB_AUTH="<Your Github personal access token>"
```

or even better, define this variable in your `.bash_profile`

## Git workflow

To understand this process you have to be familiar with `git-flow`. In our case, it's based on three main branches:

- develop
- staging
- master

All **new features or non-critical bug fixes are merged into the develop branch.** When it's time for a new release (typically at the end of the sprint), **all changes from develop are merged into staging branch**, where **we test it** thoroughly if everything is working fine. After that we **merge staging into the master branch**.

## Release process

A new release is introduced by creating a new git tag and bumping versions of packages.
These are done in a semi-automatized way with [lerna](https://github.com/lerna/lerna/tree/master/commands/version).

### Creating a Release Candidate with changelog

Over the time, we figured out these few basic steps to create a release candidate:

1. Pull the latest changes from `origin` to `master`, `staging`, `develop` branches to your local repository
2. Checkout into the `develop` branch and make sure you have all bug fixes from `master` merged to `develop`
3. Run `npx lerna-changelog` to generate a changelog and append it to the `CHANGELOG.md`
4. Commit new `CHANGELOG.md` on `develop`
5. Run `npx lerna version` and choose the appropriate version number.

   1. This will update `version` properties for all updated packages
   2. Commit all the changes
   3. Create a new `git tag` on the new commit
6. Push changes to the `origin`
7. Merge `develop` into `staging` branch
8. Push staging to `origin`
9. Create a new _Pull Request_ with a base set as `master` from `staging` branch
10. Copy changes from `CHANGELOG.md` and attach to the _PR_ body with `- [ ]` list items to render a _TODO_ list (`- [ ]` is rendered as a checkbox)
11. Let your team know that there is a _Release Candidate_ and wait until they will check every feature / bugfix they've merged
12. When all checkboxes (all features / bugfixes) are checked and the _PR_ is approved, **merge** it. **_Don't squash_** merge as it might cause conflicts for developed features

Example of _PR_ from staging to master.
![example of pr from staging to master](https://a.storyblok.com/f/117609/1520x1482/b5accb675a/example-of-pr-from-staging-to-master)

### When to release?

It's hard to say exactly. There are few points that you should think of:

- **Do not release in rush** - you should have enough time to fix any problem during and after release. There is always something unpredictable that can happen. Maybe you have forgotten to set a new environment variable or a migration has crashed.
- You should release when there are as minimum customers as possible using the product - any error can occur and that can have a negative influence on their experience with the product. Impact on the customer experience could be minimized by using strategies like [blue-green deployment](https://martinfowler.com/bliki/BlueGreenDeployment.html) or [canary releases](https://martinfowler.com/bliki/CanaryRelease.html).
- You should be **sure of the code you are releasing**.

### How often to release?

The best option is to **release in periodic intervals**, such as one sprint. Periodicity is one of the most important factors. **You don't want to release hundreds of hours of work at once**. It's much easier to release smaller features _more often_, than one big feature after two months. Last but not least, there is a customer that wants to see some progress on his project as well. **It's not a problem to release more often than you need to**, but before every release, **don't forget to check the points in the "When to release" paragraph!**

## Conclusion

In this article we've walked through the release process by **creating a changelog with minimum effort**. We've talked about a workflow and the tools we are currently using, and our method of creating a release candidate. As always, there is a lot of space to improve, e.g. it would be nice to automatize creating PR from `staging` to `master` branch with changelog description.
