mirror of
https://github.com/stablyai/orca.git
synced 2026-10-02 16:02:15 +00:00
* build(orcad): build server glibc slots on glibc 2.28 and add the glibc 2.17 compat slot
Design D6: the default linux-{x64,arm64}-glibc node-pty slots now build in
manylinux_2_28 (digest-pinned) and are gated at glibc 2.28 / GLIBCXX_3.4.25
through a floor profile on verify-linux-glibc-floor.cjs; the desktop keeps
its Ubuntu 20.04 (2.31) default.
Adds the opt-in linux-x64-glibc217 compat target: NODE_RUNTIME_COMPAT_ASSETS
pins the unofficial glibc-217 Node (update/check pin scripts cover it, outside
SERVER_TARGETS), and a new CI lane builds the compat slot in manylinux2014
with static libstdc++, gates it at glibc 2.17 with no shared C++ runtime in
DT_NEEDED, and smokes it under the glibc-217 Node.
* refactor(node-runtime-pin): route compat lookups through isCompatServerTarget; keep the glibc doc's slot-name paragraph intact
---------
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
149 lines
5.3 KiB
JavaScript
149 lines
5.3 KiB
JavaScript
import { readFileSync } from 'node:fs'
|
|
import path from 'node:path'
|
|
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
COMPAT_SERVER_TARGETS,
|
|
NODE_RUNTIME_ASSETS,
|
|
NODE_RUNTIME_COMPAT_ASSETS,
|
|
NODE_RUNTIME_PIN,
|
|
SERVER_TARGETS,
|
|
nodeRuntimeAsset,
|
|
nodeRuntimeExecutablePath
|
|
} from '../../src/shared/node-runtime-pin.ts'
|
|
import { nodeDistArchiveName } from './node-dist-archive-name.mjs'
|
|
import {
|
|
parseNodeApiVersion,
|
|
parseShasums,
|
|
pinWindowsImportLibs,
|
|
renderGeneratedBlock,
|
|
replaceGeneratedBlock,
|
|
selectAssetSource
|
|
} from './update-node-runtime-pin.mjs'
|
|
|
|
const pinFile = path.resolve(import.meta.dirname, '../../src/shared/node-runtime-pin.ts')
|
|
const HASH_A = 'a'.repeat(64)
|
|
const HASH_B = 'b'.repeat(64)
|
|
|
|
describe('pinWindowsImportLibs', () => {
|
|
it('reads each Windows node.lib hash from SHASUMS and refuses a missing one', () => {
|
|
const hashes = new Map([
|
|
['win-x64/node.lib', HASH_A],
|
|
['win-arm64/node.lib', HASH_B]
|
|
])
|
|
expect(pinWindowsImportLibs(hashes)).toEqual({
|
|
'win32-arm64': { file: 'win-arm64/node.lib', sha256: HASH_B },
|
|
'win32-x64': { file: 'win-x64/node.lib', sha256: HASH_A }
|
|
})
|
|
hashes.delete('win-arm64/node.lib')
|
|
expect(() => pinWindowsImportLibs(hashes)).toThrow('SHASUMS256.txt lists no win-arm64/node.lib')
|
|
})
|
|
})
|
|
|
|
describe('nodeDistArchiveName', () => {
|
|
it('uses nodejs.org platform names and zip only on Windows', () => {
|
|
expect(nodeDistArchiveName('24.21.0', 'linux-x64-glibc')).toBe('node-v24.21.0-linux-x64.tar.gz')
|
|
expect(nodeDistArchiveName('24.21.0', 'linux-arm64-musl')).toBe(
|
|
'node-v24.21.0-linux-arm64-musl.tar.gz'
|
|
)
|
|
expect(nodeDistArchiveName('24.21.0', 'win32-arm64')).toBe('node-v24.21.0-win-arm64.zip')
|
|
})
|
|
|
|
it('names the unofficial glibc 2.17 build for the compat target', () => {
|
|
expect(nodeDistArchiveName('24.21.0', 'linux-x64-glibc217')).toBe(
|
|
'node-v24.21.0-linux-x64-glibc-217.tar.gz'
|
|
)
|
|
})
|
|
|
|
it('covers every server and compat target', () => {
|
|
for (const target of [...SERVER_TARGETS, ...COMPAT_SERVER_TARGETS]) {
|
|
expect(nodeDistArchiveName('24.21.0', target)).not.toContain('undefined')
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('nodeRuntimeExecutablePath', () => {
|
|
it('points at bin/node on POSIX and node.exe on Windows', () => {
|
|
expect(nodeRuntimeExecutablePath('darwin-arm64', 'node-v24.21.0-darwin-arm64.tar.gz')).toBe(
|
|
'node-v24.21.0-darwin-arm64/bin/node'
|
|
)
|
|
expect(nodeRuntimeExecutablePath('win32-x64', 'node-v24.21.0-win-x64.zip')).toBe(
|
|
'node-v24.21.0-win-x64/node.exe'
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('parseShasums and selectAssetSource', () => {
|
|
const official = parseShasums(
|
|
`${HASH_A} node-v24.21.0-linux-x64-musl.tar.gz\nnot a hash line\n${HASH_A} node-v24.21.0-linux-x64.tar.gz\n`
|
|
)
|
|
const unofficial = parseShasums(
|
|
`${HASH_B} node-v24.21.0-linux-x64-musl.tar.gz\n${HASH_B} node-v24.21.0-linux-arm64-musl.tar.gz\n`
|
|
)
|
|
|
|
it('prefers the official build when both publish an archive', () => {
|
|
expect(selectAssetSource('node-v24.21.0-linux-x64-musl.tar.gz', official, unofficial)).toEqual({
|
|
source: 'official',
|
|
archiveSha256: HASH_A
|
|
})
|
|
})
|
|
|
|
it('falls back to unofficial builds and reports a missing archive as null', () => {
|
|
expect(
|
|
selectAssetSource('node-v24.21.0-linux-arm64-musl.tar.gz', official, unofficial)
|
|
).toEqual({ source: 'unofficial', archiveSha256: HASH_B })
|
|
expect(selectAssetSource('node-v24.21.0-aix-ppc64.tar.gz', official, unofficial)).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('parseNodeApiVersion', () => {
|
|
it('reads the highest supported N-API version from node_version.h', () => {
|
|
expect(
|
|
parseNodeApiVersion(
|
|
'#define NODE_API_SUPPORTED_VERSION_MAX 10\n#define NODE_API_SUPPORTED_VERSION_MIN 1\n'
|
|
)
|
|
).toBe(10)
|
|
expect(() => parseNodeApiVersion('#define NODE_MAJOR_VERSION 24')).toThrow()
|
|
})
|
|
})
|
|
|
|
describe('compat runtime targets', () => {
|
|
it('stay out of the default target list', () => {
|
|
for (const target of COMPAT_SERVER_TARGETS) {
|
|
expect(SERVER_TARGETS).not.toContain(target)
|
|
expect(NODE_RUNTIME_ASSETS).not.toHaveProperty(target)
|
|
}
|
|
})
|
|
|
|
it('resolve through nodeRuntimeAsset beside the default targets', () => {
|
|
expect(nodeRuntimeAsset('linux-x64-glibc217')).toBe(
|
|
NODE_RUNTIME_COMPAT_ASSETS['linux-x64-glibc217']
|
|
)
|
|
expect(nodeRuntimeAsset('linux-x64-glibc')).toBe(NODE_RUNTIME_ASSETS['linux-x64-glibc'])
|
|
expect(nodeRuntimeAsset('linux-riscv64-glibc')).toBeUndefined()
|
|
expect(nodeRuntimeAsset('toString')).toBeUndefined()
|
|
})
|
|
|
|
it('pin the unofficial build at its extracted bin/node', () => {
|
|
const asset = NODE_RUNTIME_COMPAT_ASSETS['linux-x64-glibc217']
|
|
expect(asset.source).toBe('unofficial')
|
|
expect(nodeRuntimeExecutablePath('linux-x64-glibc217', asset.archive)).toBe(
|
|
`node-v${NODE_RUNTIME_PIN.version}-linux-x64-glibc-217/bin/node`
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('generated block', () => {
|
|
it('reproduces the committed table byte for byte', () => {
|
|
const source = readFileSync(pinFile, 'utf8')
|
|
const regenerated = replaceGeneratedBlock(
|
|
source,
|
|
renderGeneratedBlock(NODE_RUNTIME_PIN, NODE_RUNTIME_ASSETS, NODE_RUNTIME_COMPAT_ASSETS)
|
|
)
|
|
expect(regenerated).toBe(source)
|
|
})
|
|
|
|
it('refuses a file without markers', () => {
|
|
expect(() => replaceGeneratedBlock('export {}\n', 'x')).toThrow(/markers/)
|
|
})
|
|
})
|