mirror of
https://github.com/stablyai/orca.git
synced 2026-10-03 00:02:19 +00:00
* feat(runtime): pin the Node 24.21.0 server runtime with an offline CI check Add src/shared/node-runtime-pin.ts (NODE_RUNTIME_PIN, SERVER_TARGETS, NODE_RUNTIME_ASSETS for all 8 server targets plus the headers tarball), generated by config/scripts/update-node-runtime-pin.mjs from the nodejs.org and unofficial-builds SHASUMS. check-node-runtime-pin.mjs verifies, with no network, that the pin tracks the locked Electron, matches engines.node's major, and covers exactly SERVER_TARGETS; it runs in the static analysis job. ORCAD_BUN_TARGETS consumers now read SERVER_TARGETS so there is one target list; orcad's Bun runtime and build output are unchanged. * fix(runtime): reject a pinned archive that belongs to another target --------- Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
165 lines
5.4 KiB
JavaScript
165 lines
5.4 KiB
JavaScript
import { readFileSync } from 'node:fs'
|
|
import path from 'node:path'
|
|
import { describe, expect, it } from 'vitest'
|
|
import { parse } from 'yaml'
|
|
import {
|
|
NODE_RUNTIME_ASSETS,
|
|
NODE_RUNTIME_PIN,
|
|
SERVER_TARGETS
|
|
} from '../../src/shared/node-runtime-pin.ts'
|
|
import {
|
|
findNodeRuntimePinProblems,
|
|
lockfileRootImporter,
|
|
main
|
|
} from './check-node-runtime-pin.mjs'
|
|
|
|
const projectDir = path.resolve(import.meta.dirname, '../..')
|
|
const HASH = 'a'.repeat(64)
|
|
|
|
function validInput() {
|
|
const pin = {
|
|
version: '24.21.0',
|
|
electron: '43.7.5',
|
|
napi: 10,
|
|
headers: { file: 'node-v24.21.0-headers.tar.gz', sha256: HASH }
|
|
}
|
|
const targets = ['linux-x64-glibc', 'win32-x64']
|
|
const assets = {
|
|
'linux-x64-glibc': {
|
|
source: 'official',
|
|
archive: 'node-v24.21.0-linux-x64.tar.gz',
|
|
archiveSha256: HASH,
|
|
executableSha256: HASH,
|
|
executableSize: 1
|
|
},
|
|
'win32-x64': {
|
|
source: 'official',
|
|
archive: 'node-v24.21.0-win-x64.zip',
|
|
archiveSha256: HASH,
|
|
executableSha256: HASH,
|
|
executableSize: 1
|
|
}
|
|
}
|
|
return {
|
|
pin,
|
|
assets,
|
|
targets,
|
|
packageJson: { devDependencies: { electron: '43.7.5' }, engines: { node: '24' } },
|
|
rootImporter: {
|
|
devDependencies: {
|
|
electron: { specifier: '43.7.5', version: '43.7.5(supports-color@7.2.0)' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
describe('findNodeRuntimePinProblems', () => {
|
|
it('accepts a consistent pin', () => {
|
|
expect(findNodeRuntimePinProblems(validInput())).toEqual([])
|
|
})
|
|
|
|
it('rejects an Electron bump that the pin did not follow', () => {
|
|
const input = validInput()
|
|
input.packageJson.devDependencies.electron = '43.8.0'
|
|
input.rootImporter.devDependencies.electron.version = '43.8.0'
|
|
expect(findNodeRuntimePinProblems(input)).toEqual([
|
|
'package.json electron is 43.8.0, but NODE_RUNTIME_PIN.electron is 43.7.5',
|
|
'pnpm-lock.yaml resolves electron 43.8.0, but NODE_RUNTIME_PIN.electron is 43.7.5'
|
|
])
|
|
})
|
|
|
|
it('rejects a lockfile that resolves a different Electron than package.json', () => {
|
|
const input = validInput()
|
|
input.rootImporter.devDependencies.electron.version = '43.7.6'
|
|
expect(findNodeRuntimePinProblems(input)).toEqual([
|
|
'pnpm-lock.yaml resolves electron 43.7.6, but NODE_RUNTIME_PIN.electron is 43.7.5'
|
|
])
|
|
})
|
|
|
|
it('rejects a pin major that differs from engines.node', () => {
|
|
const input = validInput()
|
|
input.packageJson.engines.node = '>=26'
|
|
expect(findNodeRuntimePinProblems(input)).toEqual([
|
|
'NODE_RUNTIME_PIN.version 24.21.0 is not package.json engines.node major 26'
|
|
])
|
|
})
|
|
|
|
it('requires exactly one asset per server target', () => {
|
|
const input = validInput()
|
|
delete input.assets['win32-x64']
|
|
input.assets['freebsd-x64'] = input.assets['linux-x64-glibc']
|
|
expect(findNodeRuntimePinProblems(input)).toEqual([
|
|
'NODE_RUNTIME_ASSETS has no entry for win32-x64',
|
|
'NODE_RUNTIME_ASSETS has freebsd-x64, which is not in SERVER_TARGETS'
|
|
])
|
|
})
|
|
|
|
it('rejects malformed hashes, sizes, sources and stale archive names', () => {
|
|
const input = validInput()
|
|
input.pin.headers.sha256 = 'ABC'
|
|
input.assets['linux-x64-glibc'] = {
|
|
source: 'mirror',
|
|
archive: 'node-v24.20.0-linux-x64.tar.gz',
|
|
archiveSha256: HASH.toUpperCase(),
|
|
executableSha256: `${HASH}0`,
|
|
executableSize: 0
|
|
}
|
|
expect(findNodeRuntimePinProblems(input)).toEqual([
|
|
'NODE_RUNTIME_PIN.headers.sha256 is not a 64-character hex SHA-256',
|
|
'linux-x64-glibc: source must be official or unofficial, got mirror',
|
|
'linux-x64-glibc: archive node-v24.20.0-linux-x64.tar.gz is not node-v24.21.0-linux-x64.tar.gz',
|
|
'linux-x64-glibc: archiveSha256 is not a 64-character hex SHA-256',
|
|
'linux-x64-glibc: executableSha256 is not a 64-character hex SHA-256',
|
|
'linux-x64-glibc: executableSize must be a positive integer'
|
|
])
|
|
})
|
|
it("rejects another target's archive", () => {
|
|
const input = validInput()
|
|
input.assets['win32-x64'].archive = 'node-v24.21.0-win-arm64.zip'
|
|
expect(findNodeRuntimePinProblems(input)).toEqual([
|
|
'win32-x64: archive node-v24.21.0-win-arm64.zip is not node-v24.21.0-win-x64.zip'
|
|
])
|
|
})
|
|
})
|
|
|
|
describe('lockfileRootImporter', () => {
|
|
it('merges the root importer across pnpm 12 lockfile documents', () => {
|
|
const contents = [
|
|
'---',
|
|
"lockfileVersion: '9.0'",
|
|
'importers:',
|
|
' .:',
|
|
' packageManagerDependencies: {}',
|
|
'---',
|
|
"lockfileVersion: '9.0'",
|
|
'importers:',
|
|
' .:',
|
|
' devDependencies:',
|
|
' electron:',
|
|
' specifier: 43.7.5',
|
|
' version: 43.7.5(supports-color@7.2.0)',
|
|
''
|
|
].join('\n')
|
|
expect(lockfileRootImporter(contents).devDependencies.electron.version).toBe(
|
|
'43.7.5(supports-color@7.2.0)'
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('committed pin', () => {
|
|
it('passes the repository check', () => {
|
|
expect(main(projectDir)).toBe(0)
|
|
})
|
|
|
|
it('covers every server target', () => {
|
|
expect(Object.keys(NODE_RUNTIME_ASSETS).sort()).toEqual([...SERVER_TARGETS].sort())
|
|
expect(NODE_RUNTIME_PIN.headers.file).toBe(`node-v${NODE_RUNTIME_PIN.version}-headers.tar.gz`)
|
|
})
|
|
|
|
it('runs in the static analysis job', () => {
|
|
const workflow = parse(readFileSync(path.join(projectDir, '.github/workflows/pr.yml'), 'utf8'))
|
|
const commands = workflow.jobs.static_analysis.steps.map((step) => step.run ?? '')
|
|
expect(commands).toContain('pnpm run check:node-runtime-pin')
|
|
})
|
|
})
|