chore: replace Lerna with npm workspaces, Turborepo and Changesets - #48
Merged
stefanoverna merged 2 commits intoAug 25, 2026
Merged
Conversation
`lerna publish` commits, tags and pushes to GitHub *before* it publishes to npm, so an expired token or a network blip on the npm side left a tag pointing at a version nobody could install, recoverable only by hand. On top of that there was no changelog, and Lerna 6 still drove the build through `lerna bootstrap`, which Lerna 7 removed. Rather than making the release smarter, remove the need for a rollback: - everything that can fail (preflight, build, tests) runs before anything is mutated, so a timeout costs nothing but the rerun; - npm is published before git is tagged, so a tag can no longer point at a version that was never released; - `changeset publish` skips packages already on the registry, so an interrupted release is resumed by re-running `npm run publish`, not undone. The resume condition asks whether *any* package is still missing from npm, which is the case a single-package check gets wrong. Along the way: - npm workspaces replace `lerna bootstrap` and the two per-package lockfiles. This also fixes what those lockfiles were pinning: react-ui was being built against a *published* `datocms-plugin-sdk@2.0.17` instead of its sibling source, and the sdk against `datocms-structured-text-utils@2.0.0` despite declaring `^5.1.16`. Both now resolve through the workspace; - Turborepo derives the build order from the manifests (sdk before react-ui); - changesets carry the release notes, with `patch` reserved for bug fixes. `datocms-plugin-sdk` and `datocms-react-ui` are a `fixed` group, so they keep moving in lockstep exactly as they did under Lerna; - `@datocms/cma-client` and `emoji-regex-xs` were declared as `"*"`, so every install resolved whatever the latest major happened to be. They are now `^5.0.0` and `^2.0.0` — the widest range that still excludes the next major, so a plugin already depending on either keeps one copy instead of two. Both floors are tested, not assumed: the build and the suite are green with `@datocms/cma-client@5.0.0` and `emoji-regex-xs@2.0.0` installed; - `datocms-react-ui` declared no `react` at all, so a fresh install could not resolve its peers; React 17 is now an explicit devDependency; - the CI workflow triggered on `main` while the default branch is `master`, so it had never run. It now runs, on Node 22/24 rather than 10-15; - the development Node floor moves from 20 (EOL) to 22, which `@changesets/cli` requires. Published packages are unaffected; - `packageManager` said `yarn@1.22.19` while CI ran `npm ci` and `yarn.lock` was gitignored. It now says what we actually use.
v4 targets Node 20, which the runners now force onto Node 24 and warn about on every run.
stefanoverna
deleted the
chore/replace-lerna-with-npm-workspaces-turborepo-changesets
branch
August 25, 2026 11:28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces Lerna with npm workspaces + Turborepo + Changesets, mirroring
datocms/js-rest-api-clients@126b710.Why
lerna publishcommits, tags and pushes to GitHub before it publishes tonpm. If the npm side then failed — expired auth, a network blip, a partial
publish across the two packages — we were left with a tag pointing at a version
nobody could install, and no way forward except by hand. There was also no
changelog, and Lerna 6 still drove the build through
lerna bootstrap, whichLerna 7 removed.
Rather than making the rollback smarter, the release no longer needs one:
master, clean tree, in sync with origin,logged in to npm, at least one pending changeset.
a timeout costs a rerun and nothing else.
changeset publishtags only whatit actually published, then
git push --follow-tags.npm run publish. The resume condition asks whether any package is stillmissing from the registry; a check on a single package gets the
partial-publish case wrong (verified against the real registry).
Decisions
npm workspaces. The repo was already inconsistent:
packageManagersaidyarn@1.22.19, CI rannpm ci, andyarn.lockwas gitignored (so deletingit is a no-op in this diff — the visible change is the
packageManagerfield).The tracked lockfiles were three, not two: a root one covering only the root
devDependencies, plus one per package.
baseBranch: "master", matching the actual default branch. Verified thatchangeset statusdiffs against it correctly.fixed: [["datocms-plugin-sdk", "datocms-react-ui"]], preserving thelockstep versioning Lerna was configured for.
(
datocms-plugin-sdk@2.2.7); the singlevX.Y.Ztag stops being created. Oldtags are untouched.
publish-next=changeset publish --tag next, notchangeset pre. It alsoworks inside pre mode, so a genuine prerelease line is one
npx changeset pre enter nextaway — andnpm run publishrefuses to run while.changeset/pre.jsonexists, so a forgotten pre mode can't silently turn a realrelease into a prerelease. Preflight is relaxed for
--tagreleases: normalreleases require
master, prereleases don't, because this repo's alphas are cutfrom feature branches (
v3.0.0-alpha.0,v3.0.1-alpha.0).Fixed along the way
packages/react-ui/package-lock.jsonpinneddatocms-plugin-sdkto aregistry copy of 2.0.17 — six versions behind. Same story inside the sdk:
datocms-structured-text-utilswas locked at 2.0.0 while the manifestdeclares
^5.1.16. Both now resolve through the workspace symlinks.datocms-react-uideclared noreactat all — no dependency, no peerdependency. The per-package lockfiles hid it; a unified install can't resolve
the peers (
react-intersection-observerwants ≤18,react-selectpullsreact-dom19). React 17 is now an explicit devDependency, which is theminimum that unblocks the install and changes nothing for consumers. The
missing
peerDependenciesis a real bug in a component library, but itchanges the published surface, so it's left for its own decision.
@datocms/cma-clientandemoji-regex-xswere declared as"*". Everyinstall resolved whatever the latest major happened to be. Now
^5.0.0and^2.0.0: the widest range that still excludes the next major, so a pluginalready depending on either keeps one copy instead of two. Both floors are
tested, not assumed — build and suite are green with
@datocms/cma-client@5.0.0and
emoji-regex-xs@2.0.0installed.mainwhile the default branch ismaster. Turning it on is therefore part of this PR, and with the old10.x–15.x matrix it would have gone red instantly: it's now 22.x/24.x, with
checkout@v4/setup-node@v4and npm caching.Heads-up: the Node floor moves from 20 to 22
@changesets/cli@3declaresengines: ^22.11 || ^24 || >=26and dies on Node 20(
enableCompileCache is not a function). Node 20 has been EOL since April..nvmrcis now22andpackageManagerisnpm@10.9.8— the same asjs-rest-api-clients, and exactly the npm that ships with Node 22.22.3.The published packages are unaffected; this only changes what you need to
develop and release. The alternative was
@changesets/cli@^2, which would haveleft our repos split across two changesets majors.
Verification
npm cifrom the regenerated lockfiledist/deleted firstdist/{cjs,esm,types}plusstyles.css,types.json,manifest.json2 cached, 2 total — 11ms >>> FULL TURBOdist/wiped,--filter=datocms-react-ui --forcepulls indatocms-plugin-sdk:buildfirst^2.2.6→^2.2.7, writes bothCHANGELOG.mds, consumes the filebin/publish.shpreflight + resume detectionmaster; resume logic checked against the real registry, including the partial-publish caseNothing was published, and no remote tags were touched.
Pre-migration diagnosis (reported, not fixed)
No drift of the three kinds worth worrying about: every published version has its
tag, no orphan tags. The gaps in the npm version lists (the SDK jumping
2.1.1→2.1.5) are just Lerna publishing only the changed package. Two thingsto know, neither of them damage:
v3.0.0-alpha.0andv3.0.1-alpha.0are tagged and published but are not onmaster— they come from a feature branch (April 2026).nextdist-tag is still on2.2.0-alpha.3, and those two 3.0.x alphascarry no dist-tag at all, so they're installable only by exact version. Which
means they didn't come out of
npm run publish-next.