mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-27 09:41:20 -08:00
build(site): [SITE-07] checkpoint frozen vendor notices and open lifecycle evidence
This commit is contained in:
1 parent
72d2b9c36b
commit
838c3eb432
23 files changed
+3964
-17
No files matched your search
@@ -0,0 +1,76 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { createHash } from 'node:crypto'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
interface Asset { path: string, sha256: string, mode: string }
|
||||
interface Notice { source: string, target: string, sha256: string }
|
||||
interface Group { name: string, version: string, tarball: string, review?: string, roots: string[], files: Asset[], notices: Notice[] }
|
||||
export interface VendorManifest { schemaVersion: number, scope: string, groups: Group[] }
|
||||
const hash = (bytes: Uint8Array) => createHash('sha256').update(bytes).digest('hex')
|
||||
|
||||
function resolve(root: string, relative: string) {
|
||||
assert(relative && !relative.includes('\\') && !path.isAbsolute(relative), 'Expected a repository-relative vendor path')
|
||||
const file = path.resolve(root, relative)
|
||||
const actual = path.relative(root, file)
|
||||
assert(actual && !actual.startsWith('..') && !path.isAbsolute(actual), 'Vendor path escapes the repository')
|
||||
return file
|
||||
}
|
||||
|
||||
function list(root: string, relative: string): string[] {
|
||||
const file = resolve(root, relative)
|
||||
const stat = fs.lstatSync(file)
|
||||
assert(!stat.isSymbolicLink(), `Redirected vendor path: ${relative}`)
|
||||
if (stat.isDirectory())
|
||||
return fs.readdirSync(file).sort().flatMap(name => list(root, `${relative}/${name}`))
|
||||
assert(stat.isFile(), `Invalid vendor file: ${relative}`)
|
||||
return [relative]
|
||||
}
|
||||
|
||||
export function generateNotices(root: string, manifest: VendorManifest): Map<string, Uint8Array> {
|
||||
assert.equal(manifest.schemaVersion, 1)
|
||||
const outputs = new Map<string, Uint8Array>()
|
||||
const summary = ['# Verified site component notices', '', manifest.scope, '', 'Generated by yarn build:site-notices. Runtime asset URLs and bytes are unchanged.', '']
|
||||
for (const group of manifest.groups) {
|
||||
assert(group.notices.length > 0, `Missing upstream notice sources: ${group.name}`)
|
||||
const actual = group.roots.flatMap(relative => list(root, relative)).sort()
|
||||
assert.deepEqual(actual, group.files.map(file => file.path).sort(), `Vendor inventory drift: ${group.name}`)
|
||||
for (const asset of group.files) {
|
||||
const bytes = fs.readFileSync(resolve(root, asset.path))
|
||||
assert(['binary', 'lf-text'].includes(asset.mode), 'Unknown asset normalization')
|
||||
const normalized = asset.mode === 'binary' ? bytes : new TextEncoder().encode(bytes.toString('utf8').replaceAll('\r\n', '\n'))
|
||||
assert.equal(hash(normalized), asset.sha256, `Vendor bytes changed: ${asset.path}`)
|
||||
}
|
||||
summary.push(`## ${group.name} ${group.version}`, '', `Source: ${group.tarball}`, '')
|
||||
if (group.review)
|
||||
summary.push(group.review, '')
|
||||
for (const notice of group.notices) {
|
||||
const bytes = fs.readFileSync(resolve(root, notice.source))
|
||||
assert.equal(hash(bytes), notice.sha256, `Upstream notice changed: ${notice.source}`)
|
||||
resolve(root, notice.target)
|
||||
assert(notice.target.startsWith(`docs/licenses/${group.name}/`), 'Notice output must stay in its component directory')
|
||||
assert(!outputs.has(notice.target), 'Duplicate notice output')
|
||||
outputs.set(notice.target, bytes)
|
||||
summary.push(`- ${notice.target.slice('docs/'.length)}`)
|
||||
}
|
||||
summary.push('')
|
||||
}
|
||||
outputs.set('docs/THIRD_PARTY_NOTICES.md', new TextEncoder().encode(`${summary.join('\n').trimEnd()}\n`))
|
||||
return outputs
|
||||
}
|
||||
|
||||
export function writeOrCheckNotices(root: string, manifest: VendorManifest, check: boolean) {
|
||||
// Resolve and verify every source before writing the first output.
|
||||
const outputs = generateNotices(root, manifest)
|
||||
for (const [relative, bytes] of outputs) {
|
||||
const file = resolve(root, relative)
|
||||
if (check) {
|
||||
assert(fs.readFileSync(file).equals(bytes), `Generated vendor notice drift: ${relative}`)
|
||||
}
|
||||
else {
|
||||
fs.mkdirSync(path.dirname(file), { recursive: true })
|
||||
fs.writeFileSync(file, bytes)
|
||||
}
|
||||
}
|
||||
return outputs.size
|
||||
}
|
||||
Reference in new issue
Block a user