2026-08-11 00:36:39 +08:00
|
|
|
/**
|
|
|
|
|
* Bump one release family's version and commit it, so the published version is
|
|
|
|
|
* readable from the repository rather than derived inside CI
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
* ([rationale](../../.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md)).
|
2026-08-11 00:36:39 +08:00
|
|
|
*
|
2026-08-19 22:22:59 +08:00
|
|
|
* The dsh family shares one version across its publishable members, private
|
|
|
|
|
* package manifests, and the workspace root:
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
* `major`, `minor`, `patch`, or an explicit `x.y.z` (including a prerelease such
|
2026-08-13 05:52:31 +08:00
|
|
|
* as `0.0.1-rc.1`). The vendored family has one version line per package, but
|
|
|
|
|
* every release advances and publishes the complete family so the next release
|
|
|
|
|
* never reuses an unchanged member's existing version from a different
|
|
|
|
|
* repository state.
|
2026-08-11 00:36:39 +08:00
|
|
|
*
|
|
|
|
|
* The version lands in the manifests, the lockfile follows, and a human creates
|
|
|
|
|
* the tag after the commit merges. CI never writes to the repository.
|
|
|
|
|
*/
|
|
|
|
|
|
2026-08-19 22:22:59 +08:00
|
|
|
import { globSync, readFileSync, writeFileSync } from 'node:fs'
|
2026-08-11 00:36:39 +08:00
|
|
|
import { join, matchesGlob } from 'node:path'
|
|
|
|
|
import { parseArgs } from 'node:util'
|
|
|
|
|
import { releaseFamily, type ReleaseFamily, type ReleaseMember } from './families.ts'
|
2026-08-13 05:52:31 +08:00
|
|
|
import { capture, isEntry } from './process.ts'
|
2026-08-11 00:36:39 +08:00
|
|
|
|
|
|
|
|
/** Files npm publishes whether or not `files` lists them. */
|
|
|
|
|
const ALWAYS_PUBLISHED = ['package.json', 'README*', 'LICENSE*', 'LICENCE*'] as const
|
|
|
|
|
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
/**
|
|
|
|
|
* Inputs that decide what a built payload contains. A package whose `files`
|
|
|
|
|
* selects `lib/` publishes build output that git does not track, so a change to
|
|
|
|
|
* the sources or the build configuration changes the tarball while no published
|
|
|
|
|
* path appears in the diff.
|
|
|
|
|
*/
|
|
|
|
|
const BUILD_INPUTS = ['src/**', 'tsconfig*.json', 'tsdown.config.*', 'build.config.*'] as const
|
|
|
|
|
|
2026-08-11 00:36:39 +08:00
|
|
|
/** Release types the dsh family accepts besides an explicit version. */
|
|
|
|
|
const RELEASE_TYPES = ['major', 'minor', 'patch'] as const
|
|
|
|
|
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
/** The workspace root manifest, which carries the dsh family's version. */
|
|
|
|
|
const ROOT_MANIFEST = 'package.json'
|
|
|
|
|
|
|
|
|
|
/** One manifest the bump rewrites, and the tag its new version will carry. */
|
|
|
|
|
interface PlannedVersion {
|
|
|
|
|
readonly manifestPath: string
|
|
|
|
|
readonly label: string
|
|
|
|
|
readonly from: string
|
|
|
|
|
readonly to: string
|
2026-08-19 22:22:59 +08:00
|
|
|
/** The tag this version publishes from, or undefined for a non-published manifest. */
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
readonly tag: string | undefined
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-19 22:22:59 +08:00
|
|
|
/** One private dsh package whose version follows the publishable family. */
|
|
|
|
|
interface PrivateDshVersion {
|
|
|
|
|
/** Repository-relative manifest path. */
|
|
|
|
|
readonly manifestPath: string
|
|
|
|
|
/** Package directory used in bump output. */
|
|
|
|
|
readonly label: string
|
|
|
|
|
/** Current manifest version. */
|
|
|
|
|
readonly version: string
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-11 00:36:39 +08:00
|
|
|
/**
|
|
|
|
|
* Split a version into its release numbers, discarding any prerelease segment.
|
|
|
|
|
* @param version - the current version.
|
|
|
|
|
* @returns Major, minor, and patch.
|
|
|
|
|
*/
|
|
|
|
|
function releaseNumbers(version: string): [number, number, number] {
|
|
|
|
|
const match = /^(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?$/.exec(version)
|
|
|
|
|
if (match === null) throw new Error(`cannot read release numbers from version ${version}`)
|
|
|
|
|
return [Number(match[1]), Number(match[2]), Number(match[3])]
|
|
|
|
|
}
|
|
|
|
|
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
/**
|
|
|
|
|
* Order two versions by their release numbers alone.
|
|
|
|
|
* @param left - one version.
|
|
|
|
|
* @param right - the other version.
|
|
|
|
|
* @returns Negative when `left` is lower, positive when higher, zero when equal.
|
|
|
|
|
*/
|
|
|
|
|
function compareReleaseNumbers(left: string, right: string): number {
|
|
|
|
|
const [leftMajor, leftMinor, leftPatch] = releaseNumbers(left)
|
|
|
|
|
const [rightMajor, rightMinor, rightPatch] = releaseNumbers(right)
|
|
|
|
|
return leftMajor - rightMajor || leftMinor - rightMinor || leftPatch - rightPatch
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-11 02:36:28 +08:00
|
|
|
/**
|
|
|
|
|
* The prerelease segment of a version, or undefined when it has none.
|
|
|
|
|
* @param version - the version to read.
|
|
|
|
|
* @returns The segment after the first `-`.
|
|
|
|
|
*/
|
|
|
|
|
function prereleaseOf(version: string): string | undefined {
|
|
|
|
|
const index = version.indexOf('-')
|
|
|
|
|
return index === -1 ? undefined : version.slice(index + 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Order two versions by semver precedence.
|
|
|
|
|
*
|
|
|
|
|
* Git's version sort cannot stand in for this: `--sort=v:refname` places
|
|
|
|
|
* `4.0.1-rc.1` above `4.0.1`, while semver gives a prerelease lower precedence
|
|
|
|
|
* than the release it precedes. Prerelease identifiers compare field by field,
|
|
|
|
|
* numeric fields numerically, so `rc.10` outranks `rc.1`.
|
|
|
|
|
* @param left - one version.
|
|
|
|
|
* @param right - the other version.
|
|
|
|
|
* @returns Negative when `left` is lower, positive when higher, zero when equal.
|
|
|
|
|
*/
|
|
|
|
|
export function compareVersions(left: string, right: string): number {
|
|
|
|
|
const numbers = compareReleaseNumbers(left, right)
|
|
|
|
|
if (numbers !== 0) return numbers
|
|
|
|
|
const leftPre = prereleaseOf(left)
|
|
|
|
|
const rightPre = prereleaseOf(right)
|
|
|
|
|
if (leftPre === undefined || rightPre === undefined) {
|
|
|
|
|
if (leftPre === rightPre) return 0
|
|
|
|
|
return leftPre === undefined ? 1 : -1
|
|
|
|
|
}
|
|
|
|
|
const leftFields = leftPre.split('.')
|
|
|
|
|
const rightFields = rightPre.split('.')
|
|
|
|
|
for (let index = 0; index < Math.max(leftFields.length, rightFields.length); index += 1) {
|
|
|
|
|
const leftField = leftFields[index]
|
|
|
|
|
const rightField = rightFields[index]
|
|
|
|
|
// A shorter identifier list has lower precedence when all its fields match.
|
|
|
|
|
if (leftField === undefined) return -1
|
|
|
|
|
if (rightField === undefined) return 1
|
|
|
|
|
if (leftField === rightField) continue
|
|
|
|
|
const leftNumeric = /^\d+$/.test(leftField)
|
|
|
|
|
const rightNumeric = /^\d+$/.test(rightField)
|
|
|
|
|
if (leftNumeric && rightNumeric) return Number(leftField) - Number(rightField)
|
|
|
|
|
// Numeric fields have lower precedence than alphanumeric ones.
|
|
|
|
|
if (leftNumeric !== rightNumeric) return leftNumeric ? -1 : 1
|
|
|
|
|
return leftField < rightField ? -1 : 1
|
|
|
|
|
}
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-11 00:36:39 +08:00
|
|
|
/**
|
|
|
|
|
* The next dsh version.
|
|
|
|
|
* @param current - the family's current shared version.
|
|
|
|
|
* @param request - `major`, `minor`, `patch`, or an explicit version.
|
|
|
|
|
* @returns The target version.
|
|
|
|
|
*/
|
|
|
|
|
function nextSharedVersion(current: string, request: string): string {
|
|
|
|
|
if (!RELEASE_TYPES.includes(request as typeof RELEASE_TYPES[number])) {
|
|
|
|
|
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(request)) {
|
|
|
|
|
throw new Error(`usage: release:dsh <major|minor|patch|x.y.z>, got ${request}`)
|
|
|
|
|
}
|
|
|
|
|
return request
|
|
|
|
|
}
|
|
|
|
|
const [major, minor, patch] = releaseNumbers(current)
|
|
|
|
|
if (request === 'major') return `${String(major + 1)}.0.0`
|
|
|
|
|
if (request === 'minor') return `${String(major)}.${String(minor + 1)}.0`
|
|
|
|
|
return `${String(major)}.${String(minor)}.${String(patch + 1)}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2026-08-11 02:36:28 +08:00
|
|
|
* The version a vendored package publishes next.
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
*
|
2026-08-13 05:52:31 +08:00
|
|
|
* The baseline is the higher of the manifest version and the last tagged
|
2026-08-11 02:36:28 +08:00
|
|
|
* version: a vendor re-sync restores upstream's version, which is lower than
|
2026-08-13 05:52:31 +08:00
|
|
|
* the release version this repository already reserved, and incrementing that
|
|
|
|
|
* would reuse an existing version.
|
2026-08-11 02:36:28 +08:00
|
|
|
*
|
|
|
|
|
* A prerelease does not consume its own release numbers. Publishing
|
|
|
|
|
* `4.0.1-rc.1` leaves `4.0.1` free, so the next stable version is `4.0.1`
|
|
|
|
|
* rather than `4.0.2`, and a second prerelease keeps those numbers too.
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
* @param current - the package's manifest version.
|
2026-08-13 05:52:31 +08:00
|
|
|
* @param tagged - the version its newest tag names, when it has one.
|
2026-08-11 02:36:28 +08:00
|
|
|
* @param prerelease - prerelease identifier to append, for a rehearsal publication.
|
2026-08-11 00:36:39 +08:00
|
|
|
* @returns The target version.
|
|
|
|
|
*/
|
2026-08-11 02:36:28 +08:00
|
|
|
export function nextVendorVersion(
|
|
|
|
|
current: string,
|
2026-08-13 05:52:31 +08:00
|
|
|
tagged: string | undefined,
|
2026-08-11 02:36:28 +08:00
|
|
|
prerelease?: string,
|
|
|
|
|
): string {
|
2026-08-13 05:52:31 +08:00
|
|
|
const taggedOrder = tagged === undefined ? undefined : compareReleaseNumbers(tagged, current)
|
|
|
|
|
const ahead = taggedOrder !== undefined && taggedOrder > 0
|
2026-08-13 06:06:45 +08:00
|
|
|
const baseline = ahead && tagged !== undefined ? tagged : current
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
const [major, minor, patch] = releaseNumbers(baseline)
|
2026-08-13 05:52:31 +08:00
|
|
|
// Reuse the numbers when the tagged version that set them is a prerelease
|
2026-08-11 02:36:28 +08:00
|
|
|
// of them; increment when a stable release already holds them.
|
2026-08-13 05:52:31 +08:00
|
|
|
const taggedPrerelease = tagged !== undefined && prereleaseOf(tagged) !== undefined
|
|
|
|
|
const sameReleasePrereleases = taggedOrder === 0 && prereleaseOf(current) !== undefined
|
|
|
|
|
const reuse = taggedPrerelease && (ahead || sameReleasePrereleases)
|
2026-08-11 02:36:28 +08:00
|
|
|
const numbers = reuse
|
|
|
|
|
? `${String(major)}.${String(minor)}.${String(patch)}`
|
|
|
|
|
: `${String(major)}.${String(minor)}.${String(patch + 1)}`
|
|
|
|
|
return prerelease === undefined ? numbers : `${numbers}-${prerelease}`
|
2026-08-11 00:36:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Whether a repository-relative path reaches the member's published payload.
|
|
|
|
|
* @param member - the member the path belongs to.
|
|
|
|
|
* @param path - repository-relative path.
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
* @returns True when `files`, npm's always-published set, or a build input selects it.
|
2026-08-11 00:36:39 +08:00
|
|
|
*/
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
export function reachesPayload(member: ReleaseMember, path: string): boolean {
|
2026-08-11 00:36:39 +08:00
|
|
|
const relative = path.slice(member.directory.length + 1)
|
|
|
|
|
const files = member.manifest.files
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
const selected = Array.isArray(files) ? files.filter((entry): entry is string => typeof entry === 'string') : []
|
|
|
|
|
const built = selected.some(pattern => pattern.startsWith('lib'))
|
|
|
|
|
const patterns = [...ALWAYS_PUBLISHED, ...selected, ...built ? BUILD_INPUTS : []]
|
2026-08-11 00:36:39 +08:00
|
|
|
return patterns.some(pattern =>
|
|
|
|
|
matchesGlob(relative, pattern) || matchesGlob(relative, `${pattern}/**`) || relative === pattern)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2026-08-13 05:52:31 +08:00
|
|
|
* The newest version a member tagged.
|
2026-08-11 00:36:39 +08:00
|
|
|
* @param family - the member's family.
|
|
|
|
|
* @param member - the member.
|
2026-08-13 05:52:31 +08:00
|
|
|
* @returns The version, or undefined when the member has no release tag.
|
2026-08-11 00:36:39 +08:00
|
|
|
*/
|
2026-08-13 05:52:31 +08:00
|
|
|
function lastTaggedVersion(family: ReleaseFamily, member: ReleaseMember): string | undefined {
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
const prefix = family.tagPrefixFor(member)
|
2026-08-11 02:36:28 +08:00
|
|
|
const versions = capture('git', ['tag', '--list', `${prefix}*`])
|
|
|
|
|
.split('\n').filter(line => line !== '').map(tag => tag.slice(prefix.length))
|
|
|
|
|
if (versions.length === 0) return undefined
|
|
|
|
|
return versions.reduce((newest, candidate) => compareVersions(candidate, newest) > 0 ? candidate : newest)
|
2026-08-11 00:36:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
* Write a version into a manifest, preserving formatting and key order.
|
2026-08-11 00:36:39 +08:00
|
|
|
* @param root - repository root.
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
* @param manifestPath - repository-relative manifest path.
|
|
|
|
|
* @param from - the version the manifest currently carries.
|
|
|
|
|
* @param to - the target version.
|
2026-08-11 00:36:39 +08:00
|
|
|
*/
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
function writeVersion(root: string, manifestPath: string, from: string, to: string): void {
|
|
|
|
|
const path = join(root, manifestPath)
|
2026-08-11 00:36:39 +08:00
|
|
|
const text = readFileSync(path, 'utf8')
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
const line = `"version": "${from}"`
|
|
|
|
|
if (!text.includes(line)) throw new Error(`${manifestPath}: cannot locate ${line}`)
|
|
|
|
|
writeFileSync(path, text.replace(line, `"version": "${to}"`))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read the workspace root version.
|
|
|
|
|
* @param root - repository root.
|
|
|
|
|
* @returns The root manifest version.
|
|
|
|
|
*/
|
|
|
|
|
function rootVersion(root: string): string {
|
|
|
|
|
const manifest: unknown = JSON.parse(readFileSync(join(root, ROOT_MANIFEST), 'utf8'))
|
|
|
|
|
const version = (manifest as Record<string, unknown>).version
|
|
|
|
|
if (typeof version !== 'string') throw new Error('package.json must declare a string version')
|
|
|
|
|
return version
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2026-08-19 22:22:59 +08:00
|
|
|
* Discover private package manifests that share the dsh version without joining
|
|
|
|
|
* its publish set.
|
|
|
|
|
* @param root - repository root.
|
|
|
|
|
* @returns Private package manifests sorted by path.
|
|
|
|
|
*/
|
|
|
|
|
function privateDshVersions(root: string): PrivateDshVersion[] {
|
2026-08-19 22:52:40 +08:00
|
|
|
return globSync('packages/*/*/package.json', { cwd: root })
|
|
|
|
|
.map(path => path.replaceAll('\\', '/'))
|
|
|
|
|
.sort()
|
|
|
|
|
.flatMap((manifestPath) => {
|
|
|
|
|
const parsed: unknown = JSON.parse(readFileSync(join(root, manifestPath), 'utf8'))
|
|
|
|
|
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
|
|
|
throw new Error(`${manifestPath} is not a JSON object`)
|
|
|
|
|
}
|
|
|
|
|
const manifest = parsed as Record<string, unknown>
|
|
|
|
|
if (manifest.private !== true) return []
|
|
|
|
|
if (typeof manifest.version !== 'string') {
|
|
|
|
|
throw new Error(`${manifestPath} must declare a string version`)
|
|
|
|
|
}
|
|
|
|
|
return [{
|
|
|
|
|
manifestPath,
|
|
|
|
|
label: manifestPath.slice(0, -'/package.json'.length),
|
|
|
|
|
version: manifest.version,
|
|
|
|
|
}]
|
|
|
|
|
})
|
2026-08-19 22:22:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Plan the dsh family's rewrite: one version for every publishable member,
|
|
|
|
|
* private package, and the root.
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
* @param family - the dsh family.
|
|
|
|
|
* @param root - repository root.
|
|
|
|
|
* @param members - the family's members.
|
|
|
|
|
* @param request - `major`, `minor`, `patch`, or an explicit version.
|
|
|
|
|
* @returns The manifests to rewrite and the shared target version.
|
|
|
|
|
*/
|
2026-08-19 22:22:59 +08:00
|
|
|
export function planShared(
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
family: ReleaseFamily,
|
|
|
|
|
root: string,
|
|
|
|
|
members: readonly ReleaseMember[],
|
|
|
|
|
request: string,
|
|
|
|
|
): { planned: PlannedVersion[]; version: string } {
|
|
|
|
|
const [first] = members
|
|
|
|
|
if (first === undefined) throw new Error(`release family ${family.id} has no members`)
|
|
|
|
|
const version = nextSharedVersion(first.version, request)
|
|
|
|
|
// The workspace root carries the family version too: the workspace constraint
|
|
|
|
|
// requires every member's version to equal the root's.
|
|
|
|
|
const planned: PlannedVersion[] = [
|
|
|
|
|
{ manifestPath: ROOT_MANIFEST, label: ROOT_MANIFEST, from: rootVersion(root), to: version, tag: undefined },
|
|
|
|
|
]
|
|
|
|
|
for (const member of members) {
|
|
|
|
|
planned.push({
|
2026-08-19 22:52:40 +08:00
|
|
|
manifestPath: `${member.directory}/package.json`,
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
label: member.directory,
|
|
|
|
|
from: member.version,
|
|
|
|
|
to: version,
|
|
|
|
|
tag: family.tagFor({ ...member, version }),
|
|
|
|
|
})
|
|
|
|
|
}
|
2026-08-19 22:52:40 +08:00
|
|
|
const publishableManifests = new Set(members.map(member => `${member.directory}/package.json`))
|
2026-08-19 22:22:59 +08:00
|
|
|
for (const entry of privateDshVersions(root)) {
|
|
|
|
|
if (publishableManifests.has(entry.manifestPath)) continue
|
|
|
|
|
planned.push({
|
|
|
|
|
manifestPath: entry.manifestPath,
|
|
|
|
|
label: entry.label,
|
|
|
|
|
from: entry.version,
|
|
|
|
|
to: version,
|
|
|
|
|
tag: undefined,
|
|
|
|
|
})
|
|
|
|
|
}
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
return { planned, version }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2026-08-13 05:52:31 +08:00
|
|
|
* Plan the vendored family's rewrite: every package advances together while
|
|
|
|
|
* retaining its own version line and tag.
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
* @param family - the vendored family.
|
|
|
|
|
* @param members - the family's members.
|
2026-08-11 02:36:28 +08:00
|
|
|
* @param prerelease - prerelease identifier to append, for a rehearsal publication.
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
* @returns The manifests to rewrite.
|
|
|
|
|
*/
|
2026-08-11 02:36:28 +08:00
|
|
|
function planPerPackage(
|
|
|
|
|
family: ReleaseFamily,
|
|
|
|
|
members: readonly ReleaseMember[],
|
|
|
|
|
prerelease: string | undefined,
|
|
|
|
|
): PlannedVersion[] {
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
const planned: PlannedVersion[] = []
|
|
|
|
|
for (const member of members) {
|
2026-08-13 05:52:31 +08:00
|
|
|
const tagged = lastTaggedVersion(family, member)
|
|
|
|
|
const to = nextVendorVersion(member.version, tagged, prerelease)
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
planned.push({
|
2026-08-19 22:52:40 +08:00
|
|
|
manifestPath: `${member.directory}/package.json`,
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
label: member.directory,
|
|
|
|
|
from: member.version,
|
|
|
|
|
to,
|
|
|
|
|
tag: family.tagFor({ ...member, version: to }),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
return planned
|
2026-08-11 00:36:39 +08:00
|
|
|
}
|
|
|
|
|
|
2026-08-11 02:36:28 +08:00
|
|
|
/**
|
|
|
|
|
* Bump the family named by `--family` and commit; `--dry-run` only reports the
|
|
|
|
|
* plan. `--prerelease rc.1` makes the vendored family publish a rehearsal
|
|
|
|
|
* version, which never takes the stable dist-tag.
|
|
|
|
|
*/
|
2026-08-11 00:36:39 +08:00
|
|
|
function main(): void {
|
|
|
|
|
const { values, positionals } = parseArgs({
|
2026-08-11 02:36:28 +08:00
|
|
|
options: {
|
|
|
|
|
family: { type: 'string' },
|
|
|
|
|
prerelease: { type: 'string' },
|
|
|
|
|
'dry-run': { type: 'boolean', default: false },
|
|
|
|
|
},
|
2026-08-11 00:36:39 +08:00
|
|
|
allowPositionals: true,
|
|
|
|
|
})
|
|
|
|
|
if (values.family === undefined) throw new Error('usage: bump.ts --family <dsh|vendor> [version]')
|
|
|
|
|
|
|
|
|
|
const family = releaseFamily(values.family)
|
|
|
|
|
const root = process.cwd()
|
|
|
|
|
const members = family.members(root)
|
|
|
|
|
family.verifyVersions(members)
|
|
|
|
|
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
let planned: PlannedVersion[]
|
2026-08-11 00:36:39 +08:00
|
|
|
let sharedVersion: string | undefined
|
|
|
|
|
if (family.id === 'dsh') {
|
|
|
|
|
const request = positionals[0]
|
|
|
|
|
if (request === undefined) throw new Error('usage: release:dsh <major|minor|patch|x.y.z>')
|
2026-08-11 02:36:28 +08:00
|
|
|
if (values.prerelease !== undefined) {
|
|
|
|
|
throw new Error('release:dsh takes the prerelease in its version argument, as in 0.0.1-rc.1')
|
|
|
|
|
}
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
const shared = planShared(family, root, members, request)
|
|
|
|
|
planned = shared.planned
|
|
|
|
|
sharedVersion = shared.version
|
2026-08-11 00:36:39 +08:00
|
|
|
} else {
|
|
|
|
|
if (positionals.length > 0) throw new Error('release:vendor takes no version: each package increments its own patch')
|
2026-08-11 02:36:28 +08:00
|
|
|
if (values.prerelease !== undefined && !/^[0-9A-Za-z.-]+$/.test(values.prerelease)) {
|
|
|
|
|
throw new Error(`--prerelease must be a semver prerelease identifier, got ${values.prerelease}`)
|
|
|
|
|
}
|
|
|
|
|
planned = planPerPackage(family, members, values.prerelease)
|
2026-08-11 00:36:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (planned.length === 0) {
|
|
|
|
|
console.log(`release bump: family ${family.id}, nothing changed since publication`)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const dryRun = values['dry-run']
|
|
|
|
|
if (!dryRun) {
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
for (const entry of planned) writeVersion(root, entry.manifestPath, entry.from, entry.to)
|
2026-08-11 00:51:02 +08:00
|
|
|
capture('pnpm', ['install', '--lockfile-only'])
|
2026-08-11 00:36:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const summary = sharedVersion
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
?? planned.map(entry => `${entry.label.replace('vendor/', '')} ${entry.to}`).join(', ')
|
2026-08-11 00:36:39 +08:00
|
|
|
console.log(`release bump: family ${family.id} -> ${summary}`)
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
for (const entry of planned) console.log(` ${entry.label}: ${entry.from} -> ${entry.to}`)
|
2026-08-11 00:36:39 +08:00
|
|
|
|
|
|
|
|
if (dryRun) {
|
|
|
|
|
console.log('release bump: dry run, nothing written')
|
|
|
|
|
return
|
|
|
|
|
}
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
capture('git', ['add', 'pnpm-lock.yaml', ...planned.map(entry => entry.manifestPath)])
|
2026-08-11 00:51:02 +08:00
|
|
|
capture('git', ['commit', '-m', `release(${family.id}): ${summary}`])
|
2026-08-11 00:36:39 +08:00
|
|
|
console.log('release bump: committed. After this merges to master, tag it:')
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
for (const tag of [...new Set(planned.map(entry => entry.tag).filter(tag => tag !== undefined))]) {
|
|
|
|
|
console.log(` git tag ${tag} <merge commit> && git push origin ${tag}`)
|
|
|
|
|
}
|
2026-08-11 00:36:39 +08:00
|
|
|
}
|
|
|
|
|
|
fix(release): close the review findings on the release sequences
The root manifest carries the dsh family version. bump writes it with the
members, because the workspace constraint requires them to match, and that
constraint now accepts a prerelease segment: without both, release:dsh 0.0.2
left the root behind and 0.0.1-rc.1 could satisfy neither check.
The Landlock workflow no longer passes --access public, which overrode the
restricted publishConfig this repository just adopted for those packages.
Vendored change detection reads build inputs when a package publishes build
output, and vendor/cordis publishes the src its export map already pointed at:
its lib/ is untracked, so a real source edit read as 'nothing changed' and the
next publish would fail on a version whose bytes moved. The next version also
takes the last published version as its baseline, so a re-sync that restores a
lower upstream version cannot recompute a version already on the registry, and
bump confirms the registry carries what the newest tag names.
Tag prefixes are constructed rather than recovered from a full tag, which a
hyphenated version defeated. Pack runs group per ref so concurrent pull requests
stop displacing each other, the publish job carries the global group, and the
unused id-token permission is gone.
Every release script sits behind an entry guard, which is what lets the pure
judgements carry tests: tag naming, publish order and cycle reporting, version
arithmetic, payload policy, and the change judgement.
The Agent Note moves to implemented and states what shipped: one probe command,
the registry confirmation that now exists, and byte reproducibility recorded as
assumed rather than measured.
2026-08-11 01:26:36 +08:00
|
|
|
if (isEntry(import.meta.url)) main()
|