Lerna monorepos with fewer tags

  Filed in: static sites, blogging

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="vf-badge">{{ componentVersion }}</span> 
- <a href="https://www.npmjs.com/package/@visual-framework/{{component}}/v/{{componentVersion}}" class="vf-badge">npm</a> 
- <a href="https://github.com/visual-framework/vf-core/commit/{{commitId}}" class="vf-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.