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
|
|
|
*
|
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 dsh family shares one version across its members and the workspace root:
|
|
|
|
|
* `major`, `minor`, `patch`, or an explicit `x.y.z` (including a prerelease such
|
|
|
|
|
* as `0.0.1-rc.1`). The vendored family has one version line per package and
|
|
|
|
|
* publishes only what changed since that package's own `vendor-<package>-v*`
|
|
|
|
|
* tag, which is the record of the commit it last published from.
|
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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { readFileSync, writeFileSync } from 'node:fs'
|
|
|
|
|
import { join, matchesGlob } from 'node:path'
|
|
|
|
|
import { parseArgs } from 'node:util'
|
|
|
|
|
import { releaseFamily, type ReleaseFamily, type ReleaseMember } from './families.ts'
|
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
|
|
|
import { attempt, 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 {
|
|
|
|
|
/** Repository-relative manifest path. */
|
|
|
|
|
readonly manifestPath: string
|
|
|
|
|
/** Label for the log line. */
|
|
|
|
|
readonly label: string
|
|
|
|
|
/** The version the manifest currently carries. */
|
|
|
|
|
readonly from: string
|
|
|
|
|
/** The version to write. */
|
|
|
|
|
readonly to: string
|
|
|
|
|
/** The tag this version publishes from, or undefined for the workspace root. */
|
|
|
|
|
readonly tag: string | undefined
|
|
|
|
|
}
|
|
|
|
|
|
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-11 02:36:28 +08:00
|
|
|
* The baseline is the higher of the manifest version and the last published
|
|
|
|
|
* version: a vendor re-sync restores upstream's version, which is lower than
|
|
|
|
|
* what this repository already published, and incrementing that would name a
|
|
|
|
|
* version the registry already carries.
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
* @param published - 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,
|
|
|
|
|
published: string | undefined,
|
|
|
|
|
prerelease?: string,
|
|
|
|
|
): string {
|
|
|
|
|
const ahead = published !== undefined && compareReleaseNumbers(published, current) > 0
|
|
|
|
|
const baseline = ahead ? published : 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-11 02:36:28 +08:00
|
|
|
// Reuse the numbers when the published version that set them is a prerelease
|
|
|
|
|
// of them; increment when a stable release already holds them.
|
|
|
|
|
const reuse = ahead && published.includes('-')
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
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 newest version a member published, read from its tags.
|
2026-08-11 00:36:39 +08:00
|
|
|
* @param family - the member's family.
|
|
|
|
|
* @param member - the member.
|
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 version, or undefined when the member never published.
|
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 lastPublishedVersion(family: ReleaseFamily, member: ReleaseMember): string | undefined {
|
|
|
|
|
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
|
|
|
* Confirm the registry carries the version a tag names.
|
|
|
|
|
*
|
|
|
|
|
* A tag is a commit pointer, not proof of publication: a tag pushed for a
|
|
|
|
|
* publication that then failed would otherwise read as "already published" and
|
|
|
|
|
* skip the package indefinitely. Querying a private package needs credentials,
|
|
|
|
|
* so an unauthenticated machine reports the gap instead of failing.
|
|
|
|
|
* @param name - package name.
|
|
|
|
|
* @param version - the version the tag names.
|
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 confirmPublished(name: string, version: string): void {
|
|
|
|
|
const result = attempt('npm', ['view', `${name}@${version}`, 'version'])
|
|
|
|
|
if (result.status === 0) return
|
|
|
|
|
const output = `${result.stdout}${result.stderr}`
|
|
|
|
|
if (output.includes('ENEEDAUTH') || output.includes('E401') || output.includes('E403')) {
|
|
|
|
|
console.log(`release bump: cannot reach the registry for ${name}@${version}; skipping the tag check`)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (output.includes('E404') || output.includes('404 Not Found')) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
`${name}@${version} is tagged but absent from the registry.`
|
|
|
|
|
+ '\nThe tag was pushed for a publication that did not complete: re-run that publish, or delete the tag.',
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
throw new Error(`npm view ${name}@${version} failed:\n${output}`)
|
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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Plan the dsh family's rewrite: one version for every member and the root.
|
|
|
|
|
* @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.
|
|
|
|
|
*/
|
|
|
|
|
function planShared(
|
|
|
|
|
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({
|
|
|
|
|
manifestPath: join(member.directory, 'package.json'),
|
|
|
|
|
label: member.directory,
|
|
|
|
|
from: member.version,
|
|
|
|
|
to: version,
|
|
|
|
|
tag: family.tagFor({ ...member, version }),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
return { planned, version }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Plan the vendored family's rewrite: every package whose payload changed since
|
|
|
|
|
* it last published.
|
|
|
|
|
* @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) {
|
|
|
|
|
const published = lastPublishedVersion(family, member)
|
|
|
|
|
if (published !== undefined) {
|
|
|
|
|
confirmPublished(member.name, published)
|
|
|
|
|
const since = `${family.tagPrefixFor(member)}${published}`
|
|
|
|
|
const changed = capture('git', ['diff', '--name-only', `${since}..HEAD`, '--', member.directory])
|
|
|
|
|
.split('\n').filter(line => line !== '')
|
|
|
|
|
if (!changed.some(path => reachesPayload(member, path))) continue
|
|
|
|
|
}
|
2026-08-11 02:36:28 +08:00
|
|
|
const to = nextVendorVersion(member.version, published, 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({
|
|
|
|
|
manifestPath: join(member.directory, 'package.json'),
|
|
|
|
|
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()
|