Lerna monorepos with fewer tags
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:
- Show the raw git history:
git show -U0 --raw
- 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))
- 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'
- Add a new post file to the
updates
directory:
--output=tools/vf-component-library/src/site/updates/$(date +%F)-component-updates.md
- 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
- Run Lerna publish and skip git tags:
lerna publish --no-git-tag-version --no-push
- Lerna publishes to npm and does cross-dependency updates to each component's
package.json
. - Commit the
package.json
updates to our main git branch - Create a manual tag
git tag -a v2.3.whatever -m 'Combined snapshot of packages'
- 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.