mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
* ci: build the WSL server template once * ci: reuse validated Windows native addons in E2E preparation * ci: avoid the unused standalone server build in SSH E2E shards
204 lines
8.0 KiB
JavaScript
204 lines
8.0 KiB
JavaScript
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { dirname, join } from 'node:path'
|
|
import { runInNewContext } from 'node:vm'
|
|
import { afterEach, expect, it, vi } from 'vitest'
|
|
import { parse } from 'yaml'
|
|
import { windowsSecurityAddonOutput } from './build-windows-security-addon.mjs'
|
|
import { stageOrcadWindowsSecurity } from './orcad-windows-security.mjs'
|
|
|
|
const roots = []
|
|
afterEach(() => roots.splice(0).forEach((root) => rmSync(root, { recursive: true, force: true })))
|
|
|
|
function peBytes(machine) {
|
|
const bytes = Buffer.alloc(0x90)
|
|
bytes.write('MZ')
|
|
bytes.writeUInt32LE(0x80, 0x3c)
|
|
bytes.write('PE\0\0', 0x80)
|
|
bytes.writeUInt16LE(machine, 0x84)
|
|
return bytes
|
|
}
|
|
|
|
function fixture() {
|
|
const root = mkdtempSync(join(tmpdir(), 'orcad-windows-security-'))
|
|
roots.push(root)
|
|
const output = join(root, 'output')
|
|
mkdirSync(output)
|
|
const write = (arch, machine) => {
|
|
const source = windowsSecurityAddonOutput(root, arch)
|
|
mkdirSync(dirname(source), { recursive: true })
|
|
writeFileSync(source, peBytes(machine))
|
|
return source
|
|
}
|
|
return { root, output, write }
|
|
}
|
|
|
|
it('stages the addon for each Windows architecture after checking its machine', () => {
|
|
const { root, output, write } = fixture()
|
|
const x64 = write('x64', 0x8664)
|
|
stageOrcadWindowsSecurity(root, output, 'win32-x64')
|
|
expect(readFileSync(join(output, 'windows-security.node'))).toEqual(readFileSync(x64))
|
|
|
|
const arm64 = write('arm64', 0xaa64)
|
|
stageOrcadWindowsSecurity(root, output, 'win32-arm64')
|
|
expect(readFileSync(join(output, 'windows-security.node'))).toEqual(readFileSync(arm64))
|
|
})
|
|
|
|
it('rejects a binary built for the wrong architecture', () => {
|
|
const { root, output, write } = fixture()
|
|
write('arm64', 0x8664)
|
|
expect(() => stageOrcadWindowsSecurity(root, output, 'win32-arm64')).toThrow('machine 0x8664')
|
|
expect(existsSync(join(output, 'windows-security.node'))).toBe(false)
|
|
})
|
|
|
|
it('fails the build off Windows when the addon was never built, and compiles it on Windows', () => {
|
|
const { root, output, write } = fixture()
|
|
expect(() => stageOrcadWindowsSecurity(root, output, 'win32-x64', { platform: 'linux' })).toThrow(
|
|
'build-windows-security-addon.mjs --arch=x64'
|
|
)
|
|
|
|
const compile = vi.fn((compileRoot, arch) => write(arch, 0x8664))
|
|
stageOrcadWindowsSecurity(root, output, 'win32-x64', { platform: 'win32' }, compile)
|
|
expect(compile).toHaveBeenCalledWith(root, 'x64', {})
|
|
expect(existsSync(join(output, 'windows-security.node'))).toBe(true)
|
|
})
|
|
|
|
it('stages nothing for non-Windows slots', () => {
|
|
const { root, output } = fixture()
|
|
stageOrcadWindowsSecurity(root, output, 'linux-x64-glibc', { platform: 'linux' })
|
|
stageOrcadWindowsSecurity(root, output, 'darwin-arm64', { platform: 'darwin' })
|
|
expect(existsSync(join(output, 'windows-security.node'))).toBe(false)
|
|
})
|
|
|
|
it.each([
|
|
['older', false],
|
|
['current', true]
|
|
])('builds and collects security addons only when the %s source ships them', (_source, ships) => {
|
|
const workflow = parse(readFileSync('.github/workflows/node-server-tests.yml', 'utf8'))
|
|
const steps = [
|
|
...workflow.jobs.persistence.steps.filter((step) =>
|
|
['Build', 'Keep'].some((verb) => step.name?.startsWith(`${verb} the Windows security addons`))
|
|
),
|
|
workflow.jobs.desktop_template.steps.find(
|
|
(step) => step.name === 'Collect the Windows security addons'
|
|
)
|
|
]
|
|
expect(steps).toHaveLength(3)
|
|
for (const step of steps) {
|
|
const hashFiles = vi.fn((path) => {
|
|
expect(path).toBe('config/scripts/build-windows-security-addon.mjs')
|
|
return ships ? 'source-hash' : ''
|
|
})
|
|
expect(
|
|
runInNewContext(step.if ?? 'true', {
|
|
inputs: { build_template: true },
|
|
matrix: { os: 'windows-2022' },
|
|
hashFiles
|
|
}),
|
|
step.name
|
|
).toBe(ships)
|
|
expect(hashFiles).toHaveBeenCalledOnce()
|
|
expect(step['continue-on-error']).toBeUndefined()
|
|
}
|
|
})
|
|
|
|
it('forwards the opt-in and pinned runtime only when compilation is needed', () => {
|
|
const { root, output, write } = fixture()
|
|
const options = { enabled: true, runtimePath: join(root, 'pinned node.exe') }
|
|
const compile = vi.fn((_root, arch) => write(arch, 0x8664))
|
|
stageOrcadWindowsSecurity(root, output, 'win32-x64', { platform: 'win32' }, compile, options)
|
|
expect(compile).toHaveBeenCalledWith(root, 'x64', options)
|
|
compile.mockClear()
|
|
stageOrcadWindowsSecurity(root, output, 'win32-x64', { platform: 'win32' }, compile, options)
|
|
expect(compile).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it.each(['ssh-windows-hosts', 'node-server-tests'])(
|
|
'opts in only in prepared Windows PR packaging in %s',
|
|
(name) => {
|
|
const workflow = parse(readFileSync(`.github/workflows/${name}.yml`, 'utf8'))
|
|
const job = workflow.jobs[name === 'ssh-windows-hosts' ? 'hosts' : 'persistence']
|
|
const preparation = job.steps.find((step) => step.id === 'dependencies')
|
|
const build = job.steps.find((step) => step.env?.ORCA_REUSE_PREPARED_WINDOWS_SECURITY)
|
|
expect(preparation.uses).toBe('./.github/actions/install-node-dependencies')
|
|
expect(preparation.with['native-runtime']).toContain('node')
|
|
expect(job.steps.indexOf(preparation)).toBeLessThan(job.steps.indexOf(build))
|
|
expect(preparation['continue-on-error']).toBeUndefined()
|
|
expect(build['continue-on-error']).toBeUndefined()
|
|
expect(build.run).toContain(
|
|
name === 'ssh-windows-hosts' ? 'build-orcad-template.mjs' : 'pnpm build:orcad'
|
|
)
|
|
for (const event of ['pull_request', 'push', 'workflow_dispatch', 'schedule']) {
|
|
for (const outcome of ['success', 'failure', 'skipped', undefined]) {
|
|
for (const os of ['Windows', 'Linux', 'macOS']) {
|
|
const enabled = runInNewContext(
|
|
build.env.ORCA_REUSE_PREPARED_WINDOWS_SECURITY.slice(3, -2),
|
|
{
|
|
github: { event_name: event },
|
|
runner: { os },
|
|
steps: { dependencies: { outcome } }
|
|
}
|
|
)
|
|
expect(enabled).toBe(
|
|
event === 'pull_request' &&
|
|
outcome === 'success' &&
|
|
(name === 'ssh-windows-hosts' || os === 'Windows')
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
)
|
|
|
|
it('opts the Windows E2E build into security reuse after Electron preparation', () => {
|
|
const workflow = parse(readFileSync('.github/workflows/e2e.yml', 'utf8'))
|
|
const job = workflow.jobs['orcad-serve-mode-switch-windows']
|
|
const preparation = job.steps.find(
|
|
(step) => step.uses === './.github/actions/install-node-dependencies'
|
|
)
|
|
const build = job.steps.find(
|
|
(step) => step.name === 'Build the e2e app, CLI, orcad slot and template'
|
|
)
|
|
expect(job['runs-on']).toBe('windows-2022')
|
|
expect(preparation.with['native-runtime']).toBe('electron')
|
|
expect(job.steps.indexOf(preparation)).toBeLessThan(job.steps.indexOf(build))
|
|
expect(preparation['continue-on-error']).toBeUndefined()
|
|
expect(build['continue-on-error']).toBeUndefined()
|
|
expect(build.env.ORCA_REUSE_PREPARED_WINDOWS_SECURITY).toBe('true')
|
|
expect(job.env.ORCA_REUSE_PREPARED_WINDOWS_SECURITY).toBeUndefined()
|
|
})
|
|
|
|
it.each([false, true])('passes an explicit CLI opt-in only when enabled: %s', (enabled) => {
|
|
const { root, output, write } = fixture()
|
|
const runtimePath = join(root, 'pinned node.exe')
|
|
const run = vi.fn(() => {
|
|
write('x64', 0x8664)
|
|
return { code: 0 }
|
|
})
|
|
stageOrcadWindowsSecurity(root, output, 'win32-x64', { platform: 'win32' }, undefined, {
|
|
enabled,
|
|
runtimePath,
|
|
run
|
|
})
|
|
expect(run).toHaveBeenLastCalledWith({
|
|
program: process.execPath,
|
|
args: [
|
|
join(root, 'config/scripts/build-windows-security-addon.mjs'),
|
|
'--arch=x64',
|
|
...(enabled ? ['--reuse-prepared-runtime', `--node-runtime=${runtimePath}`] : [])
|
|
],
|
|
cwd: root,
|
|
stdio: 'inherit',
|
|
timeoutMs: null
|
|
})
|
|
})
|
|
|
|
it('does not stage an addon when the child builder fails', () => {
|
|
const { root, output } = fixture()
|
|
const run = vi.fn(() => ({ code: 1 }))
|
|
expect(() =>
|
|
stageOrcadWindowsSecurity(root, output, 'win32-x64', { platform: 'win32' }, undefined, { run })
|
|
).toThrow('failed with exit 1')
|
|
expect(existsSync(join(output, 'windows-security.node'))).toBe(false)
|
|
})
|