Lerna monorepos with fewer tags

452 wordsFiled in: static sites, blogging

Shredded paper pile
It can be hard to get a concise view of what has changed in a monorepo release. Image by Mike Haw.

Getting the perks of monorepo publishing while curating our git tags and release notes.

For the Visual Framework, a front-end component library, we're developing its 112 npm packages as a monorepo using the tool Lerna.

Lerna works out of the box quite well, allowing us to manage all of our components in a single project and easily publishing them to npm. However, the default did two things we didn't like:

  • Each release created a new tag per updated component and we were on our way to 1,000s of tags in our project.
  • Release notes were not easy to curate and using Conventional Commits didn't suit our workflow.

What we want#

  • make one tag for each overall lerna publish release
  • use git to harvest the changes in each CHANGELOG.md to generate release notes as an "update" on the website

How we get it#

Generate the release notes#

We run the aliased command yarn run releasenotes that invokes a git dump:

git show -U0 --raw $(git describe --abbrev=0 --tags $(git rev-list --tags --skip=1  --max-count=1))..$(git describe --abbrev=0 --tags $(git rev-list --tags  --max-count=1)) --pretty=format:'commit: %H %n abbreviated_commit: %h %n subject: %s %n   sanitized_subject_line:  %f %n  date :  %aD %n  commiter : %cN %n' --output=tools/vf-component-library/src/site/updates/$(date +%F)-component-updates.md -- **/CHANGELOG.md


I'll walk through that chunk of code:

  1. Show the raw git history:
    git show -U0 --raw
  2. Since the most recent tag:
    $(git describe --abbrev=0 --tags $(git rev-list --tags --skip=1 --max-count=1))..$(git describe --abbrev=0 --tags $(git rev-list --tags --max-count=1))
  3. In this format:
    --pretty=format:'commit: %H %n abbreviated_commit: %h %n subject: %s %n sanitized_subject_line: %f %n date : %aD %n commiter : %cN %n'
  4. Add a new post file to the updates directory:
    --output=tools/vf-component-library/src/site/updates/$(date +%F)-component-updates.md
  5. Add the diff from any CHANGELOG.md changes:
    -- **/CHANGELOG.md

That still leaves us to add on the frontmatter and massage the content, but with the help of a macro it's a pretty quick task:

{% macro notes(component='vf-xxx', componentVersion='9.9.9', commitId='0123456789') %}

### [{{component}}](https://visual-framework.github.io/vf-core/components/{{component}}/)

- <span class="kh-badge">{{ componentVersion }}</span>
- <a href="https://www.npmjs.com/package/@visual-framework/{{component}}/v/{{componentVersion}}" class="kh-badge">npm</a>
- <a href="https://github.com/visual-framework/vf-core/commit/{{commitId}}" class="kh-badge">git diff</a>
{% endmacro %}


Here's an example release notes update and its 11ty source code.

Make a reference git tag for Lerna#

  1. Run Lerna publish and skip git tags: lerna publish --no-git-tag-version --no-push
  2. Lerna publishes to npm and does cross-dependency updates to each component's package.json.
  3. Commit the package.json updates to our main git branch
  4. Create a manual tag git tag -a v2.3.whatever -m 'Combined snapshot of packages'
  5. Push our new tag git push origin --tags

Now running lerna changed shows the expected "No changed packages".

It works well for us#

To be clear, we don't think there is anything "wrong" with Lerna's default publishing flow, however it didn't suit our needs. This is an alternative approach.