<link rel="stylesheet" href="/_merged_assets/_static/search/noscript.css">
Apollo Elements Apollo Elements Guides API Blog Toggle darkmode

Migrating from Lerna to Changesets

Migrating from Lerna to Changesets

Remove lerna along with associated config and packages, we don't need them anymore.

rm lerna.
yarn remove -W \
  lerna \
  cz-lerna-changelog \
  @commitlint/config-lerna-scopes

Install changesets and bootstrap the config.

yarn add -WD @changesets/cli
yarn changeset init

And since this is open source, we'll set "access" to "public" in .changeset/config.json.

Next we'll update our commitlint config:

yarn add -WD commitlint-plugin-workspace-scopes
  {
    "extends": [
      "@commitlint/config-conventional",
 -    "@commitlint/config-lerna-scopes"
 -  ]
 +  ],
 +  "plugins": ['workspace-scopes'],
 +  "rules": {
 +    "scope-enum": [2, "always", []]
 +  }
  }

Continuous Delivery

We had a flaky CD setup via lerna and GitHub actions, which would often fail horribly. Let's update that to use Changesets:

name: Release

on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2-beta
        with:
          node-version: '15'
          registry-url: 'https://registry.npmjs.org'

      - name: Install packages
        run: yarn --frozen-lockfile

      - name: Codegen
        run: yarn build:codegen

      - name: Lint
        run: yarn lint

      - name: Test
        run: yarn test

      - name: Publish Code Coverage
        uses: paambaati/codeclimate-action@v2.7.3
        env:
          CC_TEST_REPORTER_ID: $

      - name: Create Release Pull Request or Publish to npm
        id: changesets
        uses: changesets/action@master
        with:
          publish: yarn release
        env:
          GITHUB_TOKEN: $
          NPM_TOKEN: $

That yarn release script runs the following commands

yarn clean
yarn workspaces run prepublishOnly

yarn changeset publish

We also have a prerelease version of that workflow that cuts prereleases when merging to next:


name: Prerelease

on:
  push:
    branches:
      - next

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      # same setup as release.yml

      - name: Create Release Pull Request or Publish to npm
        id: changesets
        uses: changesets/action@master
        with:
          publish: yarn release
          version: yarn release:prerelease
        env:
          GITHUB_TOKEN: $
          NPM_TOKEN: $

yarn release:prerelease does

yarn changeset pre enter next
yarn changeset version
git add .
git commit -m "chore: version prerelease packages"

Previously, we did the lerna equivalent of those via npm-run-all scripts, and we had a lot of extra steps to prevent bad builds. Now we replaced all those package.json scripts with:

"release": "sh scripts/release.sh",
"release:prerelease": "sh scripts/prerelease.sh",
"postrelease": "yarn clean",

Bonus: Backfilling changesets on a large PR.

The PR which added changesets was horribly large, with no end to its scope in sight (sue me). Using git's interactive rebase command, we can add in changesets after the fact:

git rebase --interactive origin/main
edit 7f90efa5 fix(mixins): remove unused properties from query
break
pick d26b726d fix(create): mock node globals
break
pick ac74906e feat(components): add `<apollo-query>` component
pick bf485a28 feat(components): templates in `<apollo-mutation>`
pick abcdf231 feat(components): add `<apollo-subscription>`
break
pick 42bef132 test(fast): fix typings
pick 44de7bdf feat(lib)!: allow omitting keys when applying prototypes
pick a42ab820 fix(haunted): update lib version
break
pick 6a086b3d feat(hybrids)!: upgrade to v5
break
pick bbfd98b5 chore: update dependencies
pick c8663698 docs: update docs
pick 8bc127d1 chore: fix linter config
pick f6f906b5 style: lint
pick b1fed46a chore: coverage config
pick c1bbd4f0 feat(mixins)!: graphql-script-child-mixin
break
pick 462cf06c chore: migrate from lerna to changesets
pick 38b9fd1f chore: configure broken link checker action

those break commands essentially pause the rebase. Whenever git encounters one, it breaks back to the shell, which lets us add a changeset for that or for those commits.

Since my PR actually installed and configured changesets in the last commit, I needed to set up the .changeset dir before running yarn changeset on any of those breaks. Rebasing would be a chore, so instead I amended the first commit, adding in the ./changeset/config.json file even "before" installing it.

At each break, I ran yarn changeset to start the changesets wizard, and usually opened the text editor to draft the changelog portion of the changeset. I carefully studied both the existing lerna-semantic-release commit message as well as the git diff, so that the changelog accurately reflected the changes made. Once finished, I ran git commit --amend and then git rebase --continue to move on to the next one.