Files
2026-07-13 13:05:33 +08:00

90 lines
2.3 KiB
JavaScript

#!/usr/bin/env node
import { execFileSync } from 'node:child_process'
import { pathToFileURL } from 'node:url'
function gitLines(args, cwd) {
try {
return execFileSync('git', args, {
cwd,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore']
})
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean)
} catch {
return []
}
}
export function rcNumberFromTag(base, tag) {
const prefix = `v${base}-rc.`
if (!tag.startsWith(prefix)) {
return null
}
const suffix = tag.slice(prefix.length)
// Why the optional .identifier: suffixed side-branch RCs (v1.2.3-rc.4.perf)
// must advance the shared rc counter, or the next suffixed cut recomputes
// an existing tag and the workflow refuses to re-cut over it.
const match = /^(\d+)(?:\.[0-9A-Za-z]+)?$/.exec(suffix)
return match ? Number(match[1]) : null
}
export function rcNumberFromReleaseSubject(base, subject) {
const prefix = `release: v${base}-rc.`
if (!subject.startsWith(prefix)) {
return null
}
const match = /^(\d+)(?:\s|$)/.exec(subject.slice(prefix.length))
return match ? Number(match[1]) : null
}
export function highestRcForBase(base, { cwd = process.cwd() } = {}) {
const numbers = []
for (const tag of gitLines(['tag', '--list', `v${base}-rc.*`], cwd)) {
const rcNumber = rcNumberFromTag(base, tag)
if (rcNumber !== null) {
numbers.push(rcNumber)
}
}
const logRefs = ['HEAD', 'origin/main'].filter(
(ref) => gitLines(['rev-parse', '--verify', '--quiet', ref], cwd).length > 0
)
if (logRefs.length > 0) {
for (const subject of gitLines(['log', '--format=%s', ...logRefs], cwd)) {
const rcNumber = rcNumberFromReleaseSubject(base, subject)
if (rcNumber !== null) {
numbers.push(rcNumber)
}
}
}
return numbers.length === 0 ? null : Math.max(...numbers)
}
function main() {
const base = process.argv[2]
if (!base) {
throw new Error('Usage: node config/scripts/release-rc-history.mjs <base-version>')
}
const highest = highestRcForBase(base)
if (highest !== null) {
process.stdout.write(String(highest))
}
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
try {
main()
} catch (error) {
console.error(error instanceof Error ? error.message : String(error))
process.exit(1)
}
}