Conversation
gnidan
force-pushed
the
build-release-version
branch
from
September 17, 2026 06:56
6138e99 to
1a05a69
Compare
Contributor
|
gnidan
marked this pull request as draft
September 17, 2026 21:47
Lerna 8 cannot produce the release scheme this repository wants. A keyword bump on a numeric prerelease yields "alpha", because Lerna resolves the identifier as `--preid || existing || "alpha"`. An explicit version moves every workspace whose prerelease number is truthy, which is lockstep for -1 and above and changed-only for -0. One run takes one --preid, so two identifiers cannot start a series together. bin/version.ts therefore does the bump itself. Prereleases carry a named identifier: X.Y.Z-draft.N for @ethdebug/format, whose version is the specification version, and X.Y.Z-preview.N for every other workspace. The script asks `lerna changed` which workspaces changed since their tags, ignoring changelogs and test files, and forces @ethdebug/format when schemas/ changed, because the schemas live outside the package directory but ship inside it. Every dependent of a moving workspace moves too. Each moving workspace gets its own next version from semver: `prerelease` counts up, `patch` graduates every prerelease workspace, and the series-start keywords `preminor`, `premajor`, `minor` and `major` require --all and refuse to run while a prerelease exists, except that `preminor` and `premajor` may abandon a whole-repository draft series for the next one. Before it writes anything, the script reports each move with its reason, checks that the branch is main, that the tree is clean, that the nearest annotated tag is a release tag, that no tag for a new version exists, that no release tag already points at HEAD while a workspace changed, and that every changelog has a section for its workspace's new version and nothing left under Unreleased. A dry run prints the same report and exits 0. A real run then rewrites the versions and the internal dependency ranges in the package.json files, commits them as "Publish" with hooks disabled, and creates one annotated tag per moving workspace. It never pushes. If it fails after it started writing, it prints the undo commands for the stage it reached.
CI can set exactly one dist-tag per publish, because trusted
publishing covers `npm publish` and not `npm dist-tag`. A stable
version publishes under "latest", unless a higher stable version is
known, then under "release-<major>.<minor>". A prerelease publishes
under "latest" while the package has no stable version, and under its
identifier ("draft" or "preview") after that.
The known versions are the registry's list merged with the versions
from the local tags of the package. The registry document was seen to
lag minutes behind a publish; the tags do not lag, and they make a
re-run after a partial failure give the same answer.
The versioning model names the identifiers, what moves in a release, the series convention and the point at which to revisit it, and the dist-tag rule. Steps 3 and 4 of the runbook use bin/version.ts for the preview and for the bump, with a table of the keyword per phase and the filler wording for a changelog section with no changes. The tag-move recipe creates annotated tags, because `lerna changed` ignores lightweight ones. A short section records why Lerna does not bump versions here.
gnidan
force-pushed
the
build-release-version
branch
from
September 18, 2026 02:48
a388027 to
e72dc58
Compare
gnidan
marked this pull request as ready for review
September 18, 2026 02:48
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.
Prereleases get a named identifier, and
bin/version.tstakes over the version bump from Lerna.@ethdebug/formatmoves toX.Y.Z-draft.N, because a prerelease of the spec is a draft; every other workspace moves toX.Y.Z-preview.N, because those packages work but implement a draft.0.1.0-draft.0and0.1.0-preview.0sort after the numeric0.1.0-2that is published today, and both sort beforerc.Lerna 8 cannot produce this scheme, and the
0.1.0-2release showed why. A keyword bump on a numeric prerelease yieldsalpha, because Lerna resolves the identifier as--preid || existing || "alpha". An explicit version moves every workspace whose prerelease number is truthy, which is lockstep for-1and above and changed-only for-0, so the behavior flips per phase. And one run takes one--preid, sodraftandpreviewcannot start a series together. Each of those is a rule we would have to document around, so the script does the bump itself and Lerna keeps two jobs: running scripts andlerna changed.yarn tsx bin/version.ts [keyword] [--all] [--dry-run]:@ethdebug/formatwhenschemas/changed, because the schemas live outside the package directory but ship inside it; and every dependent of a moving workspace, so a spec change moves all ten. The report gives each move a reason:changed,schemas,graduates,dependentorall.prerelease(the default) counts up;patchgraduates every prerelease workspace and patches a stable one;preminor,premajor,minorandmajorstart a series, require--all, and refuse to run while any workspace is a prerelease, except thatpreminorandpremajormay abandon a whole-repository draft series for the next one.minorandmajorare guarded because on a prerelease they graduate in place or jump the series depending on the patch number.main, the tree is clean, the nearest annotated tag is a release tag (Lerna'sgit describesees annotated tags only, so a foreign tag or a lightweight release tag would hide changes), no tag for a new version exists, no release tag already points atHEADwhile a workspace changed, and every changelog has a section for its workspace's new version with nothing left under Unreleased. A dry run prints the report and the findings and exits 0; a real run stops before writing on any finding.package.jsonfiles, onePublishcommit with hooks disabled, one annotated tag per moving workspace. It never pushes; the atomic push stays the operator's step. On a failure after writing starts it prints the undo commands for the stage it reached.bin/publish-tagged.tsnow chooses the dist-tag. A stable version publishes underlatest, unless a higher stable version is known, then underrelease-<major>.<minor>. A prerelease publishes underlatestwhile the package has no stable version, and under its identifier after that, so the first0.2.0-draft.0after a stable0.1.0cannot become what a plain install gets. The known versions merge the registry's list with the local tags of the package, because the registry document was seen to lag minutes behind a publish. CI can set one tag per publish, since trusted publishing does not covernpm dist-tag.The series convention for now: all workspaces start a
major.minorseries together and graduate together with the spec; between those events each workspace moves only when it or a dependency changed, with its own counter. The runbook records the trigger to revisit it: the first time one package needs a long preview, or its own keyword, while the others release stable.Verified in a throwaway clone: the switch (
prerelease --allfrom0.1.0-2), a bugc-only release that moved bugc and its four dependents only, a schemas change that moved all ten,patchgraduation, a stable-phaseprerelease,preminor --all, a never-released workspace, and each failure path (dirty tree, wrong branch, existing tag, lightweight and foreign tags, a write failure with the right undo advice).lerna changedsees the hand-made tags on the next run. 122 tests cover the pure parts of both scripts.After this merges, one release with
prerelease --allswitches the published versions to0.1.0-draft.0and0.1.0-preview.0.