Files
dsh-web/scripts/verify-version.mjs
zhu1090093659 27e138d1f7 fix(release): bring the root alias version along with the release tag
The root package.json is private and unpublished, so the release bump never
moved its version: a git or link install of the repository kept reporting
0.1.1 in the plugin manager long after the family reached 0.4.x.

The root alias is part of the released contract, so pin its own version the
same way its aggregate dependency is pinned: verify-version now fails the
publish when the root version drifts from the tag, and the release bump
covers the root manifest beside the family packages.
2026-09-24 23:15:52 +08:00

77 lines
2.8 KiB
JavaScript

#!/usr/bin/env node
/**
* Verify every family package version and the root alias manifest match the
* release tag. The tag is the single version source of truth for the dsh-web
* release pipeline: a mismatch (e.g. a package bumped out of band, or a
* forgotten bump) fails the publish before anything reaches npm.
*
* Prints GitHub error annotations (::error file=...) on mismatch and exits
* non-zero; the release workflow runs this right before publishing.
*
* Usage: node scripts/verify-version.mjs <x.y.z|vX.Y.Z>
*/
import { readFileSync } from 'node:fs'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { walkFamilyPackages } from './lib/family-packages.mjs'
import { rootAggregatePinMismatch, rootVersionMismatch } from './lib/root-alias-pin.mjs'
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url))
const REPO_ROOT = resolve(SCRIPT_DIR, '..')
const tag = process.argv[2] ?? ''
const match = /^v?(\d+\.\d+\.\d+)$/.exec(tag)
if (match === null) {
console.error('usage: node scripts/verify-version.mjs <x.y.z | vX.Y.Z>')
process.exit(2)
}
const version = match[1]
/** Every package.json under packages/ (non-recursive, both roots). */
function packageFiles() {
return walkFamilyPackages(REPO_ROOT).map(({ pkgPath }) => pkgPath)
}
const files = packageFiles()
if (files.length === 0) {
console.error('no package.json found under packages/')
process.exit(1)
}
let mismatch = 0
for (const file of files) {
let pkgVersion
try {
pkgVersion = JSON.parse(readFileSync(file, 'utf8')).version
} catch (error) {
console.error(`::error file=${file}::unreadable package.json (${error instanceof Error ? error.message : String(error)})`)
mismatch = 1
continue
}
if (pkgVersion !== version) {
console.error(`::error file=${file}::version ${pkgVersion} does not match tag v${version}`)
mismatch = 1
}
}
// The root alias bundle is part of the released contract: its own version is
// what a git or link install reports, and its aggregate dependency must be the
// exact tag version, or an install keeping an older lockfile entry mounts patch
// rows the installed aggregate cannot export (issue #1442).
try {
const rootManifest = JSON.parse(readFileSync(resolve(REPO_ROOT, 'package.json'), 'utf8'))
const rootMismatches = [rootVersionMismatch(rootManifest, version), rootAggregatePinMismatch(rootManifest, version)]
for (const message of rootMismatches) {
if (message !== undefined) {
console.error(`::error file=package.json::${message}`)
mismatch = 1
}
}
} catch (error) {
console.error(`::error file=package.json::unreadable root package.json (${error instanceof Error ? error.message : String(error)})`)
mismatch = 1
}
if (mismatch) process.exit(1)
console.log(`[verify-version] all ${files.length} packages, the root version and the root aggregate pin match v${version}`)