mirror of
https://gh-proxy.org/https://github.com/klinsmaya/workdsh.git
synced 2026-11-09 18:07:02 +08:00
163 lines
13 KiB
JavaScript
163 lines
13 KiB
JavaScript
/** Materialize one frozen public runtime graph. Does not install or run package hooks. */
|
|
import { createHash } from 'node:crypto'
|
|
import { copyFileSync, chmodSync, existsSync, globSync, lstatSync, mkdirSync, readdirSync, readFileSync, readlinkSync, realpathSync, symlinkSync, writeFileSync } from 'node:fs'
|
|
import { dirname, join, relative, resolve } from 'node:path'
|
|
import { spawnSync } from 'node:child_process'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { contained, inspectFixedRuntimeGraph, readJson, requireRuntime } from './fixed-runtime-graph.mjs'
|
|
import { inspectOwnedRuntimeImports } from './fixed-runtime-imports.mjs'
|
|
|
|
export const sha256 = path => createHash('sha256').update(readFileSync(path)).digest('hex')
|
|
export const jsonFile = (path, value) => writeFileSync(path, JSON.stringify(value, null, 2) + '\n')
|
|
export const packageDirectory = (root, node) => join(root, 'node_modules/.fixed', node.id, 'node_modules', node.name)
|
|
const deniedName = name => name === 'node_modules' || name === '.git' || name === '.npmrc' || name.startsWith('.env') || name === '.yarnrc.yml'
|
|
function safeRelative(value) { return typeof value === 'string' && value !== '' && !value.includes('\\') && !value.startsWith('/') && value.split('/').every(p => p !== '..' && p !== '') }
|
|
|
|
/** Include package shipping roots, never source workspace configuration or caches. */
|
|
export function shippingPaths(node) {
|
|
const selected = new Set(['package.json'])
|
|
const add = part => {
|
|
requireRuntime(safeRelative(part), 'shipping-path-refused')
|
|
if (part.split('/').some(deniedName)) return
|
|
const path = join(node.source, part), info = lstatSync(path)
|
|
if (info.isDirectory()) for (const name of readdirSync(path).sort()) add(`${part}/${name}`)
|
|
else { requireRuntime(info.isFile() || info.isSymbolicLink(), 'shipping-file-kind-refused'); selected.add(part) }
|
|
}
|
|
if (node.firstParty) {
|
|
requireRuntime(Array.isArray(node.manifest.files), 'first-party-shipping-list-missing')
|
|
for (const pattern of node.manifest.files) {
|
|
requireRuntime(safeRelative(pattern), 'shipping-pattern-refused')
|
|
for (const part of globSync(pattern, { cwd: node.source }).sort()) add(part)
|
|
}
|
|
for (const name of readdirSync(node.source).sort()) if (/^(?:licen[cs]e|copying|notice)(?:\.|$)/iu.test(name)) add(name)
|
|
} else for (const name of readdirSync(node.source).sort()) if (!deniedName(name)) add(name)
|
|
return [...selected].sort()
|
|
}
|
|
export function inventory(root) {
|
|
const entries = []; let bytes = 0, visited = 0
|
|
const walk = part => {
|
|
const path = join(root, part), info = lstatSync(path)
|
|
requireRuntime(++visited <= 250000 && part.split('/').length <= 64, 'payload-tree-budget-exceeded')
|
|
if (info.isDirectory()) for (const name of readdirSync(path).sort()) walk(part ? `${part}/${name}` : name)
|
|
else if (info.isSymbolicLink()) {
|
|
const target = readlinkSync(path)
|
|
requireRuntime(!target.startsWith('/') && contained(root, resolve(dirname(path), target)), 'link-escaped')
|
|
requireRuntime(contained(root, realpathSync(path)), 'link-target-escaped')
|
|
entries.push({ path: part, kind: 'link', target })
|
|
} else {
|
|
requireRuntime(info.isFile(), 'file-kind-refused')
|
|
bytes += info.size
|
|
requireRuntime(entries.length < 150000 && bytes <= 3 * 1024 ** 3 && info.size <= 256 * 1024 ** 2, 'payload-byte-budget-exceeded')
|
|
entries.push({ path: part, kind: 'file', bytes: info.size, mode: info.mode & 0o777, sha256: sha256(path) })
|
|
}
|
|
}
|
|
walk(''); return entries
|
|
}
|
|
function link(target, path) { mkdirSync(dirname(path), { recursive: true }); symlinkSync(relative(dirname(path), target), path, 'dir') }
|
|
function normalizedPackage(node) {
|
|
const result = { ...node.manifest }
|
|
delete result.devDependencies; delete result.scripts; delete result.packageManager; delete result.devEngines
|
|
for (const field of ['dependencies', 'peerDependencies', 'optionalDependencies']) if (result[field]) {
|
|
result[field] = { ...result[field] }
|
|
for (const [name, edge] of Object.entries(node.edges)) if (Object.hasOwn(result[field], name)) result[field][name] =
|
|
result[field][name].startsWith('npm:') ? `npm:${edge.providerName}@${edge.version}` : edge.version
|
|
}
|
|
for (const [name, edge] of Object.entries(node.edges)) if (edge.supplementalRuntimeImport) {
|
|
result.dependencies ??= {}
|
|
result.dependencies[name] = edge.declared.startsWith('npm:') ? `npm:${edge.providerName}@${edge.version}` : edge.version
|
|
}
|
|
return result
|
|
}
|
|
export function prepareFixedRuntime({ source, manifestPath, nodeArchive, output }) {
|
|
source = realpathSync(source); output = resolve(output)
|
|
const manifest = readJson(manifestPath)
|
|
requireRuntime(process.platform === 'linux' && process.arch === 'x64' && Boolean(process.report.getReport().header.glibcVersionRuntime), 'linux-glibc-x64-build-required')
|
|
requireRuntime(!existsSync(output) && !contained(source, output), 'output-must-be-new-and-isolated')
|
|
const head = spawnSync('git', ['rev-parse', 'HEAD'], { cwd: source, encoding: 'utf8' })
|
|
requireRuntime(head.status === 0 && head.stdout.trim() === manifest.sourceRevision, 'source-head-mismatch')
|
|
const clean = spawnSync('git', ['status', '--porcelain', '--untracked-files=no'], { cwd: source, encoding: 'utf8', timeout: 10000 })
|
|
requireRuntime(clean.status === 0 && clean.stdout.trim() === '', 'source-tracked-changes-refused')
|
|
requireRuntime(sha256(nodeArchive) === manifest.node.sha256, 'node-archive-hash-mismatch')
|
|
const primaryLockPath = 'deepseek-harness/scripts/primary-runtime/lock.json', primaryLock = readJson(join(source, primaryLockPath))
|
|
requireRuntime(primaryLock.nodeVersion === manifest.node.version && primaryLock.targets[manifest.target]?.nodeSha256 === manifest.node.sha256
|
|
&& manifest.node.url === `https://nodejs.org/dist/v${primaryLock.nodeVersion}/node-v${primaryLock.nodeVersion}-${primaryLock.targets[manifest.target].nodeArchive}`, 'upstream-node-reference-mismatch')
|
|
const policyPaths = ['yarn.lock', 'workdsh-web/pnpm-lock.yaml', 'enterprise/model-adapter/pnpm-lock.yaml', 'deepseek-harness/pnpm-lock.yaml', 'workdsh-web/package.json', primaryLockPath]
|
|
const policyInputs = policyPaths.map(path => ({ path, bytes: lstatSync(join(source, path)).size, sha256: sha256(join(source, path)) }))
|
|
const overrides = readJson(join(source, 'workdsh-web/package.json')).pnpm?.overrides ?? {}
|
|
const graph = inspectFixedRuntimeGraph({ source, manifest }), inputFiles = [], packageRecords = []
|
|
let bytes = 0, files = 0
|
|
mkdirSync(dirname(output), { recursive: true, mode: 0o700 })
|
|
mkdirSync(output, { recursive: false, mode: 0o700 })
|
|
for (const node of graph.nodes) {
|
|
for (const file of node.runtimeImportFiles ?? []) requireRuntime(sha256(join(node.source, file.file)) === file.sha256, 'source-import-file-changed')
|
|
const destination = packageDirectory(output, node), selected = shippingPaths(node)
|
|
mkdirSync(destination, { recursive: true })
|
|
const paths = new Set(selected)
|
|
for (const part of selected) {
|
|
const from = join(node.source, part), to = join(destination, part), info = lstatSync(from)
|
|
mkdirSync(dirname(to), { recursive: true })
|
|
if (info.isSymbolicLink()) {
|
|
const target = readlinkSync(from), resolved = resolve(dirname(from), target)
|
|
requireRuntime(!target.startsWith('/') && contained(node.source, resolved) && paths.has(relative(node.source, realpathSync(from))), 'package-link-target-not-shipped')
|
|
symlinkSync(target, to); continue
|
|
}
|
|
bytes += info.size; files++
|
|
requireRuntime(bytes <= 2 * 1024 ** 3 && files <= 150000 && info.size <= 256 * 1024 ** 2, 'shipping-budget-exceeded')
|
|
inputFiles.push({ path: relative(source, from), bytes: info.size, sha256: sha256(from) })
|
|
if (part === 'package.json') {
|
|
requireRuntime(sha256(from) === node.sourcePackageSha256, 'package-metadata-changed')
|
|
jsonFile(to, normalizedPackage(node))
|
|
}
|
|
else { copyFileSync(from, to); chmodSync(to, info.mode & 0o755) }
|
|
}
|
|
for (const [name, edge] of Object.entries(node.edges)) link(packageDirectory(output, graph.nodes.find(n => n.id === edge.id)), join(destination, 'node_modules', name))
|
|
packageRecords.push({ id: node.id, name: node.name, version: node.version, path: relative(output, destination), edges: node.edges, optionalMissing: node.optionalMissing,
|
|
...(node.runtimeImports ? { runtimeImports: node.runtimeImports, dynamicRuntimeImports: node.dynamicRuntimeImports } : {}) })
|
|
}
|
|
for (const [name, id] of Object.entries(graph.roots)) link(packageDirectory(output, graph.nodes.find(n => n.id === id)), join(output, 'node_modules', name))
|
|
// Public entry requirements that are transitive-only still have a fixed root provider.
|
|
for (const name of ['@deepseek-ai/dsh-app-boot']) {
|
|
const node = graph.nodes.find(n => n.name === name); requireRuntime(node, 'entry-provider-missing')
|
|
link(packageDirectory(output, node), join(output, 'node_modules', name))
|
|
}
|
|
jsonFile(join(output, 'package.json'), { name: manifest.name, version: manifest.version, private: true, type: 'module', dependencies: { ...manifest.roots } })
|
|
mkdirSync(join(output, 'bin')); const archiveRoot = `node-v${manifest.node.version}-${manifest.target}`
|
|
const extracted = spawnSync('tar', ['-xzf', resolve(nodeArchive), '--strip-components=1', '-C', output, `${archiveRoot}/bin/node`, `${archiveRoot}/LICENSE`], { encoding: 'utf8', timeout: 30000 })
|
|
requireRuntime(extracted.status === 0, 'node-selected-extract-failed')
|
|
chmodSync(join(output, 'bin/node'), 0o755)
|
|
const nodeVersion = spawnSync(join(output, 'bin/node'), ['--version'], { encoding: 'utf8', timeout: 10000 })
|
|
requireRuntime(nodeVersion.status === 0 && nodeVersion.stdout.trim() === `v${manifest.node.version}`, 'packaged-node-version-mismatch')
|
|
const metadata = { ...manifest, packages: packageRecords, roots: graph.roots, omitted: graph.omitted,
|
|
sourceAliases: graph.sourceAliases.map(a => ({ ...a, source: relative(source, a.source) })) }
|
|
jsonFile(join(output, 'fixed-runtime.json'), metadata)
|
|
mkdirSync(join(output, 'audit'))
|
|
jsonFile(join(output, 'audit/owned-runtime-imports.json'), inspectOwnedRuntimeImports(output, metadata))
|
|
// Bounded chunks carry complete source-byte provenance without one huge Library leaf.
|
|
for (let i = 0; i < inputFiles.length; i += 1000) jsonFile(join(output, 'audit', `source-${String(i / 1000).padStart(3, '0')}.json`), inputFiles.slice(i, i + 1000))
|
|
jsonFile(join(output, 'audit/source-policy.json'), { sourceRevision: manifest.sourceRevision, buildNode: process.version,
|
|
upstream: readJson(join(source, 'upstream.json')), nodeArchiveSha256: manifest.node.sha256,
|
|
inputLocks: policyInputs.filter(input => input.path.endsWith('lock') || input.path.endsWith('lock.yaml')),
|
|
policyInputs, overrides, inputFiles: inputFiles.length, inputBytes: bytes })
|
|
// Detect source mutation during copying rather than signing a mixed revision.
|
|
for (const file of inputFiles) requireRuntime(sha256(join(source, file.path)) === file.sha256, 'source-changed-during-materialization')
|
|
for (const file of policyInputs) requireRuntime(sha256(join(source, file.path)) === file.sha256, 'source-policy-changed')
|
|
const finalHead = spawnSync('git', ['rev-parse', 'HEAD'], { cwd: source, encoding: 'utf8' })
|
|
requireRuntime(finalHead.status === 0 && finalHead.stdout.trim() === manifest.sourceRevision, 'source-head-changed')
|
|
const finalClean = spawnSync('git', ['status', '--porcelain', '--untracked-files=no'], { cwd: source, encoding: 'utf8', timeout: 10000 })
|
|
requireRuntime(finalClean.status === 0 && finalClean.stdout.trim() === '', 'source-tracked-changes-during-build')
|
|
const entries = inventory(output)
|
|
const chunks = []
|
|
for (let i = 0; i < entries.length; i += 1000) {
|
|
const path = `audit/payload-${String(i / 1000).padStart(3, '0')}.json`
|
|
jsonFile(join(output, path), entries.slice(i, i + 1000)); chunks.push({ path, sha256: sha256(join(output, path)), bytes: lstatSync(join(output, path)).size })
|
|
}
|
|
jsonFile(join(output, 'runtime-integrity.json'), { format: 1, chunks, entries: entries.length,
|
|
payloadBytes: entries.reduce((sum, entry) => sum + (entry.bytes ?? 0), 0), enterpriseActivation: manifest.enterpriseActivation })
|
|
return { output, packages: graph.nodes.length, files, inputBytes: bytes, integritySha256: sha256(join(output, 'runtime-integrity.json')) }
|
|
}
|
|
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
const [source, manifestPath, nodeArchive, output] = process.argv.slice(2)
|
|
requireRuntime(source && manifestPath && nodeArchive && output && process.argv.length === 6, 'usage:source-manifest-archive-output')
|
|
console.log(JSON.stringify(prepareFixedRuntime({ source, manifestPath, nodeArchive, output })))
|
|
}
|