mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 09:07:40 +00:00
* ci: reuse prepared Windows security addon in PR packages * ci: prepare bounded Windows security reuse qualification * ci: ignore owned Windows benchmark receipts during source checks * test: inject Windows security qualification dependencies * test(mobile): register chat view mocks in test roots
179 lines
6.0 KiB
JavaScript
179 lines
6.0 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Compile native/windows-security for an orcad Windows slot.
|
|
*
|
|
* orcad runs under its own pinned Node with no node_modules, and its secure-file writes and
|
|
* data-root privacy check need this addon: without it the privacy check fails closed and orcad
|
|
* refuses to start. N-API, so one build serves every Node. Arm64 cross-compiles from x64.
|
|
*
|
|
* Builds in a copy of the sources so the desktop's own build/Release (the host arch) survives.
|
|
*
|
|
* node config/scripts/build-windows-security-addon.mjs --arch=arm64
|
|
*/
|
|
import { copyFileSync, cpSync, existsSync, mkdirSync, rmSync } from 'node:fs'
|
|
import { createRequire } from 'node:module'
|
|
import { isAbsolute, join, resolve } from 'node:path'
|
|
import { pathToFileURL } from 'node:url'
|
|
import { runProcessSync } from '@orca/process-host'
|
|
import { NODE_RUNTIME_ASSETS } from '../../src/shared/node-runtime-pin.ts'
|
|
import { sha256Of } from './orcad-prebuild-slot-contents.mjs'
|
|
import { ORCAD_WINDOWS_SECURITY_FILENAME } from '../../src/shared/orcad-artifacts.ts'
|
|
|
|
const { PE_MACHINE, describePeMachine, readPeMachine } = createRequire(import.meta.url)(
|
|
'./windows-pe-machine.cjs'
|
|
)
|
|
|
|
const ROOT = resolve(import.meta.dirname, '..', '..')
|
|
const SUPPORTED_ARCHES = ['x64', 'arm64']
|
|
|
|
export function windowsSecurityAddonOutput(root, arch) {
|
|
return join(root, '.build', 'windows-security', arch, ORCAD_WINDOWS_SECURITY_FILENAME)
|
|
}
|
|
|
|
export function probeWindowsSecurityAddon(
|
|
runtimePath,
|
|
candidate,
|
|
arch,
|
|
run = runProcessSync,
|
|
cwd = ROOT
|
|
) {
|
|
return run({
|
|
program: runtimePath,
|
|
args: [
|
|
'-e',
|
|
`if (process.platform !== 'win32' || process.arch !== ${JSON.stringify(arch)} || process.versions.electron) process.exit(1);` +
|
|
`const sid = require(${JSON.stringify(candidate)}).currentUserSid();` +
|
|
`if (typeof sid !== 'string' || !/^S-1-\\d+(?:-\\d+)+$/.test(sid)) process.exit(2);` +
|
|
`process.stdout.write('windows-security-ready');`
|
|
],
|
|
cwd,
|
|
timeoutMs: 30_000
|
|
})
|
|
}
|
|
|
|
export function reusePreparedWindowsSecurityAddon(
|
|
arch,
|
|
output,
|
|
{
|
|
enabled = false,
|
|
runtimePath,
|
|
root = ROOT,
|
|
platform = process.platform,
|
|
hostArch = process.arch,
|
|
runtimeAssets = NODE_RUNTIME_ASSETS,
|
|
run = runProcessSync
|
|
} = {}
|
|
) {
|
|
if (!enabled || platform !== 'win32' || arch !== hostArch || !runtimePath) {
|
|
return false
|
|
}
|
|
const candidate = resolve(
|
|
root,
|
|
'native/windows-security/build/Release/orca_windows_security.node'
|
|
)
|
|
try {
|
|
if (
|
|
readPeMachine(candidate) !== PE_MACHINE[arch] ||
|
|
!isAbsolute(runtimePath) ||
|
|
sha256Of(runtimePath) !== runtimeAssets[`win32-${arch}`]?.executableSha256
|
|
) {
|
|
return false
|
|
}
|
|
const prepared = run({
|
|
program: process.execPath,
|
|
args: [
|
|
join(root, 'config/scripts/ensure-native-runtime.mjs'),
|
|
'--runtime=node',
|
|
'--check-only'
|
|
],
|
|
cwd: root,
|
|
timeoutMs: 30_000
|
|
})
|
|
if (prepared.code !== 0 || prepared.timedOut || prepared.outputTruncated) {
|
|
return false
|
|
}
|
|
const probe = probeWindowsSecurityAddon(runtimePath, candidate, arch, run, root)
|
|
if (
|
|
probe.code !== 0 ||
|
|
probe.timedOut ||
|
|
probe.outputTruncated ||
|
|
probe.stdout !== 'windows-security-ready'
|
|
) {
|
|
return false
|
|
}
|
|
} catch {
|
|
return false
|
|
}
|
|
mkdirSync(resolve(output, '..'), { recursive: true })
|
|
copyFileSync(candidate, output)
|
|
console.log(`[windows-security] reused prepared ${arch} -> ${output}`)
|
|
return true
|
|
}
|
|
|
|
export function buildWindowsSecurityAddon(
|
|
arch,
|
|
output = windowsSecurityAddonOutput(ROOT, arch),
|
|
{ root = ROOT, run = runProcessSync, ...options } = {}
|
|
) {
|
|
if (!SUPPORTED_ARCHES.includes(arch)) {
|
|
throw new Error(`--arch must be one of ${SUPPORTED_ARCHES.join(', ')}; got ${arch}`)
|
|
}
|
|
if (process.platform !== 'win32') {
|
|
throw new Error(
|
|
`The Windows security addon only builds on Windows; running on ${process.platform}.`
|
|
)
|
|
}
|
|
if (reusePreparedWindowsSecurityAddon(arch, output, { ...options, root, run })) {
|
|
return output
|
|
}
|
|
// Inside the repo so binding.gyp's `require('node-addon-api')` resolves from root node_modules.
|
|
const workDir = join(root, '.build', 'windows-security', arch, 'gyp')
|
|
rmSync(workDir, { recursive: true, force: true })
|
|
mkdirSync(workDir, { recursive: true })
|
|
copyFileSync(join(root, 'native/windows-security/binding.gyp'), join(workDir, 'binding.gyp'))
|
|
cpSync(join(root, 'native/windows-security/src'), join(workDir, 'src'), { recursive: true })
|
|
const result = run({
|
|
program: process.execPath,
|
|
args: [
|
|
join(root, 'node_modules', 'node-gyp', 'bin', 'node-gyp.js'),
|
|
'rebuild',
|
|
`--arch=${arch}`
|
|
],
|
|
cwd: workDir,
|
|
stdio: 'inherit',
|
|
timeoutMs: null
|
|
})
|
|
if (result.code !== 0) {
|
|
throw new Error(`node-gyp failed for ${arch} with exit ${result.code}`)
|
|
}
|
|
const built = join(workDir, 'build', 'Release', 'orca_windows_security.node')
|
|
if (!existsSync(built)) {
|
|
throw new Error(`node-gyp reported success but ${built} is missing.`)
|
|
}
|
|
// Why check the artifact: node-gyp can ignore --arch, and a host could not load the result.
|
|
const machine = readPeMachine(built)
|
|
if (machine !== PE_MACHINE[arch]) {
|
|
throw new Error(`Built binary is ${describePeMachine(machine)}, expected ${arch}.`)
|
|
}
|
|
mkdirSync(resolve(output, '..'), { recursive: true })
|
|
copyFileSync(built, output)
|
|
console.log(`[windows-security] ${arch} -> ${output}`)
|
|
return output
|
|
}
|
|
|
|
if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
|
|
try {
|
|
const arch =
|
|
process.argv.find((arg) => arg.startsWith('--arch='))?.slice('--arch='.length) ?? process.arch
|
|
buildWindowsSecurityAddon(arch, undefined, {
|
|
enabled: process.argv.includes('--reuse-prepared-runtime'),
|
|
runtimePath: process.argv
|
|
.find((arg) => arg.startsWith('--node-runtime='))
|
|
?.slice('--node-runtime='.length)
|
|
})
|
|
} catch (error) {
|
|
console.error(`[windows-security] ${error instanceof Error ? error.message : String(error)}`)
|
|
process.exit(1)
|
|
}
|
|
}
|