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,54 @@
|
||||
// Downloads official pinned archives only. Never executes downloaded Windows files.
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import sevenZip from '7zip-bin'
|
||||
import { transformRuntime } from './runtime-transform.mjs'
|
||||
import { projectRoot, runtimeRoot, manifest, digest, inventory, verifyRuntimeBundle } from './runtime-bundle.mjs'
|
||||
const cache = path.join(projectRoot, '.tmp/runtime-downloads')
|
||||
await fs.mkdir(cache, { recursive: true })
|
||||
const stage = await fs.mkdtemp(path.join(runtimeRoot, '.prepare-'))
|
||||
try {
|
||||
if (process.platform !== 'win32') await fs.chmod(sevenZip.path7za, 0o755)
|
||||
for (const runtime of manifest.runtimes) {
|
||||
const archive = path.join(cache, runtime.archive)
|
||||
let bytes = await fs.readFile(archive).catch(() => null)
|
||||
if (!bytes) {
|
||||
console.log(`Downloading ${runtime.id} ${runtime.version} from ${runtime.url}`)
|
||||
const response = await fetch(runtime.url, { signal: AbortSignal.timeout(300_000) })
|
||||
if (!response.ok) throw new Error(`Download failed: ${response.status} ${runtime.url}`)
|
||||
bytes = Buffer.from(await response.arrayBuffer())
|
||||
}
|
||||
if (digest(bytes, runtime.algorithm, runtime.digestEncoding) !== runtime.digest) throw new Error(`Archive integrity failure: ${runtime.id}; remove ${archive} to redownload`)
|
||||
await fs.writeFile(archive, bytes)
|
||||
const unpack = path.join(stage, `unpack-${runtime.id}`)
|
||||
await fs.mkdir(unpack)
|
||||
const result = spawnSync(sevenZip.path7za, ['x', archive, `-o${unpack}`, '-y'], { encoding: 'utf8', windowsHide: true })
|
||||
if (result.error || result.status !== 0) throw new Error(`Archive extraction failed: ${runtime.id}: ${result.error || result.stderr || result.stdout}`)
|
||||
const target = path.join(stage, runtime.id)
|
||||
await fs.rename(path.join(unpack, runtime.prefix), target)
|
||||
await transformRuntime(runtime.id, target)
|
||||
// No loose mutation of the extraction lock. A source update requires reviewed tree digest changes.
|
||||
const tree = digest(JSON.stringify(await inventory(target)))
|
||||
if (runtime.treeSha256 && tree !== runtime.treeSha256) throw new Error(`Extracted ${runtime.id} tree mismatch: ${tree}`)
|
||||
if (!runtime.treeSha256) throw new Error(`Unpinned ${runtime.id} extracted tree: ${tree}`)
|
||||
}
|
||||
await verifyRuntimeBundle(stage)
|
||||
const destination = path.join(runtimeRoot, 'win32-x64')
|
||||
const previous = `${destination}.previous`
|
||||
await fs.rm(previous, { recursive: true, force: true })
|
||||
const exists = await fs.stat(destination).then(() => true, () => false)
|
||||
if (exists) await fs.rename(destination, previous)
|
||||
try {
|
||||
await fs.mkdir(destination)
|
||||
for (const runtime of manifest.runtimes) await fs.rename(path.join(stage, runtime.id), path.join(destination, runtime.id))
|
||||
} catch (error) {
|
||||
await fs.rm(destination, { recursive: true, force: true })
|
||||
if (exists) await fs.rename(previous, destination)
|
||||
throw error
|
||||
}
|
||||
await fs.rm(previous, { recursive: true, force: true })
|
||||
console.log('Windows x64 runtimes prepared; native Windows smoke tests still required.')
|
||||
} finally {
|
||||
await fs.rm(stage, { recursive: true, force: true })
|
||||
}
|
||||
Reference in new issue
Block a user