feat: bundle private Windows runtimes with settings switches
This commit is contained in:
1 parent
759df7060d
commit
bef87520c2
28 files changed
+902
-10
No files matched your search
@@ -0,0 +1,43 @@
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import crypto from 'node:crypto'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
export const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||
export const runtimeRoot = path.join(projectRoot, 'resources/runtimes')
|
||||
export const manifest = JSON.parse(await fs.readFile(path.join(runtimeRoot, 'manifest.json'), 'utf8'))
|
||||
export const digest = (bytes, algorithm = 'sha256', encoding = 'hex') => crypto.createHash(algorithm).update(bytes).digest(encoding)
|
||||
export async function inventory(root) {
|
||||
const files = {}
|
||||
async function visit(dir) {
|
||||
for (const entry of (await fs.readdir(dir, { withFileTypes: true })).sort((a,b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
|
||||
const absolute = path.join(dir, entry.name)
|
||||
if (entry.isSymbolicLink()) throw new Error(`Runtime symlink is not allowed: ${absolute}`)
|
||||
if (entry.isDirectory()) await visit(absolute)
|
||||
else if (entry.isFile()) files[path.relative(root, absolute).split(path.sep).join('/')] = digest(await fs.readFile(absolute))
|
||||
else throw new Error(`Unsupported runtime entry: ${absolute}`)
|
||||
}
|
||||
}
|
||||
await visit(root)
|
||||
return files
|
||||
}
|
||||
export async function verifyRuntimeBundle(root = path.join(runtimeRoot, 'win32-x64')) {
|
||||
for (const runtime of manifest.runtimes) {
|
||||
const dir = path.join(root, runtime.id)
|
||||
for (const required of runtime.required) {
|
||||
const file = path.join(dir, required)
|
||||
const bytes = await fs.readFile(file).catch(() => { throw new Error(`Missing bundled ${runtime.id}: ${required}. Run npm run runtimes:prepare.`) })
|
||||
if (!bytes.length) throw new Error(`Empty runtime asset: ${file}`)
|
||||
if (required.endsWith('.exe')) {
|
||||
if (bytes.length < 64 || bytes.subarray(0, 2).toString() !== 'MZ') throw new Error(`Invalid Windows executable: ${file}`)
|
||||
const pe = bytes.readUInt32LE(0x3c)
|
||||
if (pe > bytes.length - 6 || bytes.subarray(pe, pe + 4).toString('hex') !== '50450000' || bytes.readUInt16LE(pe + 4) !== 0x8664) {
|
||||
throw new Error(`Runtime executable is not Windows x64 PE: ${file}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
const files = await inventory(dir)
|
||||
const actual = digest(JSON.stringify(files))
|
||||
if (!runtime.treeSha256 || actual !== runtime.treeSha256) throw new Error(`Bundled ${runtime.id} tree checksum mismatch: ${actual}; expected ${runtime.treeSha256}. Run npm run runtimes:prepare.`)
|
||||
console.log(`${runtime.id} ${runtime.version}: verified ${Object.keys(files).length} files (${actual})`)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user