mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-12 06:26:16 -08:00
docs(refactor): BASE-07 track compatibility risks and third-party provenance
This commit is contained in:
1 parent
e01f39ffa0
commit
ed293231dc
11 files changed
+2077
-20
No files matched your search
@@ -0,0 +1,94 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import lockfile from '@yarnpkg/lockfile'
|
||||
import { hash, refactorDir } from './releases.mjs'
|
||||
|
||||
const root = path.resolve(refactorDir, '..')
|
||||
const read = name => JSON.parse(fs.readFileSync(path.join(refactorDir, name), 'utf8'))
|
||||
export function referencedFindings() {
|
||||
const files = ['baselines/lifecycle-coverage.md', 'baselines/dom-coverage.md', 'baselines/consumer-coverage.md', 'baselines/performance-coverage.md']
|
||||
return [...new Set(files.flatMap(name => fs.readFileSync(path.join(refactorDir, name), 'utf8').match(/BASE-(?:LIFE|DOM|DEMO|TYPE|DIST|PERF)-\d{2}/g) || []))].sort()
|
||||
}
|
||||
function evidenceExists(name) {
|
||||
const absolute = path.resolve(root, name)
|
||||
assert(absolute.startsWith(root + path.sep) && fs.existsSync(absolute), `Missing or invalid evidence: ${name}`)
|
||||
}
|
||||
export function validateRisks(register) {
|
||||
const tasks = new Set(read('tasks.json').tasks.map(task => task.id))
|
||||
const ids = new Set(register.items.map(item => item.id))
|
||||
assert.equal(ids.size, register.items.length, 'Duplicate risk ID')
|
||||
for (const id of referencedFindings()) assert(ids.has(id), `Unregistered baseline finding: ${id}`)
|
||||
for (const item of register.items) {
|
||||
assert(['reproduced', 'source-observed', 'unverified'].includes(item.confirmation))
|
||||
assert(['open', 'resolved', 'accepted-with-scope'].includes(item.status))
|
||||
assert(item.owners.length && item.owners.every(id => tasks.has(id)), `Unowned risk: ${item.id}`)
|
||||
assert(item.evidence.length && item.compatibleResolution && item.closureCriteria, `Incomplete risk: ${item.id}`)
|
||||
item.evidence.forEach(evidenceExists)
|
||||
if (item.status !== 'open') {
|
||||
assert(item.resolutionEvidence?.length && item.resolutionRationale, `Premature closure: ${item.id}`)
|
||||
item.resolutionEvidence.forEach(evidenceExists)
|
||||
}
|
||||
}
|
||||
}
|
||||
export function assetFingerprints(inputs) {
|
||||
const walk = name => {
|
||||
const absolute = path.resolve(root, name)
|
||||
evidenceExists(name)
|
||||
return fs.statSync(absolute).isDirectory() ? fs.readdirSync(absolute).flatMap(child => walk(`${name}/${child}`)) : [name]
|
||||
}
|
||||
return inputs.flatMap(walk).sort().map(name => {
|
||||
const bytes = fs.readFileSync(path.join(root, name))
|
||||
const text = /\.(?:[cm]?[jt]s|css|less|json|html|svg|txt|md)$/.test(name)
|
||||
return { path: name, algorithm: text ? 'sha256-lf' : 'sha256', sha256: hash(text ? bytes.toString('utf8').replaceAll('\r\n', '\n') : bytes) }
|
||||
})
|
||||
}
|
||||
export function validateThirdParty(inventory, register) {
|
||||
const tasks = new Set(read('tasks.json').tasks.map(task => task.id))
|
||||
const risks = new Set(register.items.map(item => item.id))
|
||||
const initial = read('package-inventory.json').packages.map(pkg => pkg.name).sort()
|
||||
assert.deepEqual(inventory.packages.map(pkg => pkg.name).sort(), initial, 'External dependency package coverage missing')
|
||||
const lock = lockfile.parse(fs.readFileSync(path.join(root, 'yarn.lock'), 'utf8')).object
|
||||
for (const pkg of inventory.packages) {
|
||||
const current = JSON.parse(fs.readFileSync(path.join(root, 'packages', pkg.name, 'package.json'), 'utf8'))
|
||||
assert.deepEqual(pkg.declaredRuntimeDependencies, current.dependencies || {}, `Update dependency inventory: ${pkg.name}`)
|
||||
assert.deepEqual(pkg.developmentDependencies, current.devDependencies || {}, `Update development dependency inventory: ${pkg.name}`)
|
||||
assert.deepEqual(pkg.resolvedRuntimeDependencies.map(dependency => dependency.name).sort(), Object.keys(current.dependencies || {}).sort(), 'Missing resolved dependency')
|
||||
for (const dependency of pkg.resolvedRuntimeDependencies) {
|
||||
assert.equal(dependency.range, current.dependencies[dependency.name], 'Dependency range changed')
|
||||
const installed = JSON.parse(fs.readFileSync(path.join(root, 'node_modules', dependency.name, 'package.json'), 'utf8'))
|
||||
assert.equal(dependency.lockedVersion, lock[`${dependency.name}@${dependency.range}`]?.version, 'Dependency lock changed; update provenance')
|
||||
assert.equal(dependency.installedVersion, installed.version, 'Installed dependency changed')
|
||||
assert.equal(dependency.manifestLicense, installed.license || null, 'Dependency license metadata changed')
|
||||
}
|
||||
}
|
||||
assert.deepEqual(inventory.vendored.map(vendor => vendor.riskId).sort(), [...risks].filter(id => id.startsWith('VENDOR-')).sort(), 'Missing vendored group')
|
||||
assert.deepEqual(inventory.integrations.map(sdk => sdk.riskId).sort(), [...risks].filter(id => id.startsWith('SDK-')).sort(), 'Missing SDK integration')
|
||||
for (const vendor of inventory.vendored) {
|
||||
assert(risks.has(vendor.riskId) && vendor.owners.length && vendor.owners.every(id => tasks.has(id)))
|
||||
assert(vendor.sourceStatus && vendor.licenseStatus && vendor.updatePolicy)
|
||||
assert.deepEqual(assetFingerprints(vendor.inputs), vendor.fingerprints, `Vendored files changed without reviewed provenance: ${vendor.id}`)
|
||||
}
|
||||
for (const sdk of inventory.integrations) {
|
||||
assert(risks.has(sdk.riskId) && sdk.owners.length && sdk.owners.every(id => tasks.has(id)))
|
||||
assert(sdk.sourceFiles.length && sdk.validationStatus && sdk.contract)
|
||||
sdk.sourceFiles.forEach(evidenceExists)
|
||||
}
|
||||
}
|
||||
export function renderRisks(register) {
|
||||
const labels = { reproduced: '已复现', 'source-observed': '源码/产物事实', unverified: '待取证' }
|
||||
const escape = text => text.replaceAll('|', '\\|').replaceAll('\n', ' ')
|
||||
return `# 重构风险与差异索引\n\n由 risks.json 生成;运行 node refactor/scripts/risk-register.mjs --write 更新。状态 open 表示尚缺关闭证据,不等于每项都是已复现缺陷。处理方案、关闭条件与证据见 [机器台账](risks.json),来源与范围见 [维护说明](risk-guide.md)。\n\n| ID | 状态 / 证据等级 | 条目 | 后续任务 |\n| --- | --- | --- | --- |\n${register.items.map(item => `| ${item.id} | ${item.status} / ${labels[item.confirmation]} | ${escape(item.title)} | ${item.owners.join(', ')} |`).join('\n')}\n`
|
||||
}
|
||||
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
||||
assert(process.argv.slice(2).every(arg => ['--check', '--write'].includes(arg)), 'Use --check or --write')
|
||||
const register = read('risks.json')
|
||||
const inventory = read('third-party.json')
|
||||
validateRisks(register)
|
||||
validateThirdParty(inventory, register)
|
||||
const rendered = renderRisks(register)
|
||||
if (process.argv.includes('--write')) fs.writeFileSync(path.join(refactorDir, 'risk-table.md'), rendered)
|
||||
else assert.equal(fs.readFileSync(path.join(refactorDir, 'risk-table.md'), 'utf8').replaceAll('\r\n', '\n'), rendered, 'Regenerate risk-table.md')
|
||||
console.log(`Risk register checked: ${register.items.length} items, ${inventory.vendored.length} vendored groups, ${inventory.integrations.length} integrations; no unresolved item is waived`)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import test from 'node:test'
|
||||
import { renderRisks, validateRisks, validateThirdParty } from './risk-register.mjs'
|
||||
import { refactorDir } from './releases.mjs'
|
||||
|
||||
const read = name => JSON.parse(fs.readFileSync(path.join(refactorDir, name), 'utf8'))
|
||||
test('Risk register rejects omitted findings, missing owners and unsupported closure', () => {
|
||||
const register = read('risks.json')
|
||||
validateRisks(register)
|
||||
assert.equal(fs.readFileSync(path.join(refactorDir, 'risk-table.md'), 'utf8').replaceAll('\r\n', '\n'), renderRisks(register))
|
||||
for (const mutate of [
|
||||
value => value.items = value.items.filter(item => item.id !== 'BASE-LIFE-04'),
|
||||
value => value.items[0].owners = ['NO-SUCH-TASK'],
|
||||
value => value.items[0].status = 'resolved',
|
||||
value => value.items[0].evidence = ['../outside-workspace'],
|
||||
]) {
|
||||
const changed = structuredClone(register)
|
||||
mutate(changed)
|
||||
assert.throws(() => validateRisks(changed))
|
||||
}
|
||||
})
|
||||
test('Third-party provenance cannot silently omit copied assets, SDKs or change fingerprints', () => {
|
||||
const register = read('risks.json')
|
||||
const inventory = read('third-party.json')
|
||||
validateThirdParty(inventory, register)
|
||||
for (const mutate of [
|
||||
value => value.packages.pop(),
|
||||
value => value.vendored.pop(),
|
||||
value => value.integrations.pop(),
|
||||
value => value.vendored[0].fingerprints[0].sha256 = 'incorrect',
|
||||
value => value.packages.find(pkg => pkg.resolvedRuntimeDependencies.length).resolvedRuntimeDependencies[0].lockedVersion = '0.0.0',
|
||||
]) {
|
||||
const changed = structuredClone(inventory)
|
||||
mutate(changed)
|
||||
assert.throws(() => validateThirdParty(changed, register))
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user