test(release): [REL-04] checkpoint renamed rollback and mainline conflict recovery

This commit is contained in:
Harvey Zhao committed 2026-09-15 00:28:33 +08:00
1 parent aa29dc9212
commit d0c33f6258
14 files changed
+1718 -8

No files matched your search

+75
View File
@@ -0,0 +1,75 @@
import assert from 'node:assert/strict'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import { ensureArchive, readMember } from './releases.mjs'
const root = fileURLToPath(new URL('../../', import.meta.url))
assert(['--write', '--check'].includes(process.argv[2]) && process.argv.length === 3, 'Use --write or --check')
const read = file => JSON.parse(fs.readFileSync(path.join(root, file), 'utf8'))
const ledger = read('refactor/release-ledger.json')
const rows = []
for (const pkg of ledger.packages) {
const baseline = read(pkg.history.file)
const release = pkg.history.selector?.split('.').reduce((value, key) => value?.[key], baseline)
const current = read(`packages/${pkg.name}/package.json`)
const row = { name: pkg.name, targetVersion: pkg.targetVersion, distribution: pkg.distribution, history: pkg.history, currentDependencies: current.dependencies || {}, currentPeers: current.peerDependencies || {}, currentOptionalDependencies: current.optionalDependencies || {} }
if (pkg.distribution === 'site') {
rows.push({ ...row, restore: 'Previously verified Pages artifact, exact source SHA and URL map; npm rollback does not apply.', status: 'previous-deployment-artifact-required', evidence: 'refactor/pages-deployment.md' })
continue
}
if (pkg.distribution === 'recovered-npm') {
assert(release, 'Missing recovered history')
rows.push({ ...row, restore: 'No intact historical npm tarball. Preserve a complete pre-upgrade application/artifact snapshot and verify it before any candidate deployment.', status: 'complete-rollback-artifact-required', evidence: pkg.history.file })
continue
}
assert(release?.integrity && release.sha256 && release.files, `Missing frozen rollback archive: ${pkg.name}`)
const archive = await ensureArchive(release)
const manifest = JSON.parse(readMember(archive, 'package/package.json'))
assert.equal(manifest.name, release.name)
assert.equal(manifest.version, release.version)
rows.push({
...row,
previous: { name: release.name, version: release.version, tarball: release.tarball, integrity: release.integrity, sha256: release.sha256, memberCount: Object.keys(release.files).length, dependencies: manifest.dependencies || {}, peerDependencies: manifest.peerDependencies || {}, optionalDependencies: manifest.optionalDependencies || {} },
exactDependency: { [release.name]: release.version },
restore: pkg.distribution === 'renamed-npm'
? 'Restore application import code, old package name and saved lock together; npm alias alone does not restore the old export/helper surface.'
: 'Restore the saved application manifest/lock and matching artifacts, then frozen-install the previous exact version with its tested core/plugin combination.',
status: 'archive-verified-batch-rehearsal-required',
})
}
assert.equal(rows.length, 22)
const result = { task: 'REL-04', source: 'refactor/release-ledger.json and frozen historical archives', registryAvailabilityRefreshed: false, mutableTagPolicy: 'Use exact versions and saved lock/artifact hashes. No latest/next/rollback tag is assumed or changed.', packages: rows }
const json = `${JSON.stringify(result, null, 2)}\n`
const dependencies = value => Object.entries(value).map(([name, version]) => `${name}@${version}`).join(', ') || '无声明'
const lines = [
'# 逐包回退清单',
'',
'由 `node refactor/scripts/rollback-inventory.mjs --write` 生成;`--check` 校验同步并复核冻结 tarball 完整性。',
'',
'本表核对 20 份完整历史 archive 的 SHA-512/SHA-256 和 manifest。它不表示当前 registry 可用,',
'也不表示全部包的实际回退已验收。机器清单同时保存旧/新 dependencies、peerDependencies 与精确 integrity。',
'',
'| 当前包 | 目标版本 | 恢复版本/对象 | 旧包运行依赖 | 恢复状态 |',
'| --- | --- | --- | --- | --- |',
...rows.map(row => `| ${row.name} | ${row.targetVersion} | ${row.previous ? `${row.previous.name}@${row.previous.version}` : row.distribution === 'site' ? '已验证 Pages 产物' : '完整旧产物尚缺'} | ${row.previous ? dependencies(row.previous.dependencies) : '见原有门槛'} | ${row.status} |`),
'',
'每包优先恢复升级前已验证的应用代码、package.json 和 yarn.lock,再执行 `yarn install --frozen-lockfile`;',
'离线恢复还需保存所需 tarball/依赖缓存。单独把版本改回去并重新解析依赖,不能替代已测试锁文件。',
'外部播放器 SDK、worker/WASM、远端媒体和平台能力也要恢复对应配置,npm 依赖表不覆盖它们。',
'',
'iframe 必须同步恢复旧包名、CommonJS `.default`、helper 路径和应用代码;不将 npm alias 当作兼容门面。',
'Thumbnail 完整原包缺失以及站点部署恢复仍是发布门槛。正式每批重新绑定候选和回退内容,不能直接复用早期演练结果。',
'',
'来源:[机器清单](baselines/rollback-inventory.json)、[维护入口](rollback-rehearsal.md)、[发布台账](release-ledger.md)。',
'',
]
const markdown = lines.join('\n')
for (const [file, contents] of [['refactor/baselines/rollback-inventory.json', json], ['refactor/rollback-inventory.md', markdown]]) {
if (process.argv[2] === '--write')
fs.writeFileSync(path.join(root, file), contents)
else
assert.equal(fs.readFileSync(path.join(root, file), 'utf8').replaceAll('\r\n', '\n'), contents, `Stale rollback inventory: ${file}`)
}
console.log(`Rollback inventory: ${rows.length} packages, ${rows.filter(row => row.previous).length} verified historical archives, remaining batch gates retained`)