mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
74 lines
4.6 KiB
JavaScript
74 lines
4.6 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { execFileSync } from 'node:child_process'
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import process from 'node:process'
|
|
import { readJson, workspace, writeJson } from './package-consumer.mjs'
|
|
import { digest, inside } from './pages/artifact.ts'
|
|
import { replaceRehearsalSite, verifyRestoredTree } from './pages/recovery.ts'
|
|
|
|
assert.equal(process.versions.node, fs.readFileSync(path.join(workspace, '.node-version'), 'utf8').trim(), 'Use pinned Node')
|
|
const baseline = readJson(path.join(workspace, 'refactor/baselines/pages-artifact-validation.json'))
|
|
const archived = baseline.remote.rollbackArchive
|
|
assert(/^[a-f0-9]{40}$/.test(archived.commit))
|
|
const previousArchive = inside(workspace, archived.path)
|
|
assert.deepEqual(digest(previousArchive), { bytes: archived.bytes, sha256: archived.sha256 }, 'Frozen Pages archive changed')
|
|
const git = args => execFileSync('git', args, { cwd: workspace, encoding: 'utf8', windowsHide: true })
|
|
assert.equal(git(['rev-parse', '--show-object-format']).trim(), 'sha1', 'Recovery verifier expects Git SHA-1 object IDs')
|
|
const tree = git(['ls-tree', '-r', '-l', '-z', archived.commit]).split('\0').filter(Boolean)
|
|
const expected = {}
|
|
for (const entry of tree) {
|
|
const match = /^(100644|100755) blob ([a-f0-9]{40}) +(\d+)\t(.+)$/s.exec(entry)
|
|
assert(match, 'Site snapshot contains unsupported Git entries')
|
|
const [, , objectId, bytes, relative] = match
|
|
inside(workspace, relative)
|
|
assert(!Object.hasOwn(expected, relative), 'Duplicate Git path')
|
|
expected[relative] = { objectId, bytes: Number(bytes) }
|
|
}
|
|
const parent = path.join(workspace, 'refactor/.cache/pages-recovery')
|
|
fs.mkdirSync(parent, { recursive: true })
|
|
const output = fs.mkdtempSync(path.join(parent, 'run-'))
|
|
const report = { task: 'REL-04', capturedAt: new Date().toISOString(), source: git(['rev-parse', 'HEAD']).trim(), node: process.versions.node, passed: false, archived, gitFiles: expected, scope: 'Local restoration of the complete frozen gh-pages snapshot; no deployment or remote setting changes' }
|
|
try {
|
|
const archive = path.join(output, 'canonical.zip')
|
|
const archiveArgs = ['-c', 'core.autocrlf=false', '-c', 'core.eol=lf', 'archive', '--format=zip', `--output=${archive}`, archived.commit]
|
|
git(archiveArgs)
|
|
report.canonicalArchive = { command: ['git', ...archiveArgs], ...digest(archive) }
|
|
const members = execFileSync('tar', ['-tf', archive], { encoding: 'utf8', windowsHide: true }).trim().split(/\r?\n/)
|
|
assert.equal(new Set(members).size, members.length)
|
|
for (const member of members) inside(workspace, member.replace(/\/$/, ''))
|
|
assert.deepEqual(members.filter(member => !member.endsWith('/')).sort(), Object.keys(expected).sort(), 'Archive inventory differs from frozen Git tree')
|
|
const original = path.join(output, 'original')
|
|
const site = path.join(output, 'site')
|
|
const prepared = path.join(output, 'prepared')
|
|
fs.mkdirSync(original)
|
|
execFileSync('tar', ['-xf', archive, '-C', original], { windowsHide: true })
|
|
const before = verifyRestoredTree(original, expected)
|
|
assert.equal(fs.readFileSync(path.join(original, 'CNAME'), 'utf8').trim(), 'github.artplayer.org')
|
|
fs.cpSync(original, site, { recursive: true })
|
|
fs.writeFileSync(path.join(site, 'compiled/artplayer.js'), 'broken candidate fixture\n')
|
|
fs.writeFileSync(path.join(site, 'CNAME'), 'recovery-fixture.invalid\n')
|
|
fs.unlinkSync(path.join(site, 'iframe.html'))
|
|
fs.writeFileSync(path.join(site, 'candidate-only.txt'), 'must disappear after restoration\n')
|
|
assert.throws(() => verifyRestoredTree(site, expected), /missing or additional files/)
|
|
fs.cpSync(original, prepared, { recursive: true })
|
|
const restored = replaceRehearsalSite(output, expected)
|
|
assert.deepEqual(restored, before)
|
|
assert.equal(fs.readFileSync(path.join(site, 'CNAME'), 'utf8').trim(), 'github.artplayer.org')
|
|
assert.equal(fs.readFileSync(path.join(output, 'failed-site/compiled/artplayer.js'), 'utf8'), 'broken candidate fixture\n')
|
|
assert(!fs.existsSync(path.join(site, 'candidate-only.txt')))
|
|
report.files = restored
|
|
report.simulatedChanges = ['modified compiled/artplayer.js', 'changed CNAME', 'deleted iframe.html', 'added candidate-only.txt']
|
|
report.failedSnapshotRetained = true
|
|
report.passed = true
|
|
}
|
|
catch (error) {
|
|
report.error = { name: error.name, message: error.message }
|
|
throw error
|
|
}
|
|
finally {
|
|
writeJson(path.join(output, 'report.json'), report)
|
|
writeJson(path.join(parent, 'latest.json'), { output: path.relative(workspace, output).replaceAll('\\', '/'), passed: report.passed })
|
|
console.log(`Pages recovery: ${path.relative(workspace, output)}; passed=${report.passed}`)
|
|
}
|