mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 18:17:36 +00:00
build(runtime): target Node 24 while retaining host compatibility (#34177)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -21,6 +21,12 @@ Install Node 24, pnpm, and the Bun version in [`config/.bun-version`](../config/
|
||||
native PTYs, socket liveness, and V8 memory behavior. `pnpm test:node` runs the same suites
|
||||
entirely on Node. Builds and dependency installation still use Node and pnpm.
|
||||
|
||||
The server, its workers and its packaged dependencies target Node 24. The compatibility
|
||||
launcher and CLI still target Node 18; the launcher hands off to bundled Node 24 before
|
||||
loading server code. WSL guest programs and host discovery retain their Node 18 minimum.
|
||||
Keep Node 24 APIs and server imports out of these host compatibility paths. Running an
|
||||
unpackaged server directly requires Node 24 or newer.
|
||||
|
||||
Use the pnpm version in `packageManager` in [`package.json`](../package.json): `corepack enable`
|
||||
needs Corepack 0.34.5 or newer to start the pinned pnpm (`npm install -g corepack@latest`). On
|
||||
Windows it writes shims next to `node.exe` and fails with `EPERM` outside an elevated shell when
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
buildOrcadEntry,
|
||||
buildOrcadCli,
|
||||
buildOrcadLauncher,
|
||||
ORCAD_SERVER_BUILD_TARGET,
|
||||
ORCAD_BUNDLE_PLUGINS,
|
||||
ORCAD_EXTERNAL_MODULES,
|
||||
ORCAD_CHILD_ENTRY_POINTS,
|
||||
@@ -117,7 +118,7 @@ async function stageParcelWatcher(target) {
|
||||
},
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
target: 'node18',
|
||||
target: ORCAD_SERVER_BUILD_TARGET,
|
||||
format: 'cjs',
|
||||
outfile: join(OUT_DIR, ORCAD_PARCEL_WATCHER_ENTRY),
|
||||
external: ['./watcher.node'],
|
||||
@@ -222,7 +223,7 @@ function buildForkedChild(entryPoint, outfile) {
|
||||
entryPoints: [entryPoint],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
target: 'node18',
|
||||
target: ORCAD_SERVER_BUILD_TARGET,
|
||||
format: 'cjs',
|
||||
outfile,
|
||||
external: ORCAD_EXTERNAL_MODULES,
|
||||
|
||||
@@ -10,6 +10,9 @@ const { turnedOnDarkBuildDefines } = createRequire(import.meta.url)('../dark-bui
|
||||
|
||||
export const ORCAD_ENTRY_POINT = 'src/main/orcad/main.ts'
|
||||
export const ORCAD_LAUNCHER_ENTRY_POINT = 'src/main/orcad/launcher.ts'
|
||||
export const ORCAD_SERVER_BUILD_TARGET = 'node24'
|
||||
// Host entry points must load before Orca selects its bundled runtime.
|
||||
const ORCAD_COMPAT_BUILD_TARGET = 'node18'
|
||||
export const ORCAD_CHILD_ENTRY_POINTS = {
|
||||
watcher: 'src/main/ipc/filesystem/watcher-process/parcel-watcher-process-entry.ts',
|
||||
daemon: 'src/main/daemon/daemon-entry.ts',
|
||||
@@ -141,7 +144,7 @@ function buildOrcadEntryBundle(outfile, minify) {
|
||||
entryPoints: [join(root, ORCAD_ENTRY_POINT)],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
target: 'node18',
|
||||
target: ORCAD_SERVER_BUILD_TARGET,
|
||||
format: 'cjs',
|
||||
outfile,
|
||||
external: ORCAD_EXTERNAL_MODULES,
|
||||
@@ -164,7 +167,7 @@ export function buildOrcadCli(outfile) {
|
||||
entryPoints: [join(root, 'src/cli/index.ts')],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
target: 'node18',
|
||||
target: ORCAD_COMPAT_BUILD_TARGET,
|
||||
format: 'cjs',
|
||||
outfile,
|
||||
plugins: ORCAD_BUNDLE_PLUGINS,
|
||||
@@ -184,7 +187,7 @@ export function buildOrcadLauncher(outfile) {
|
||||
entryPoints: [join(root, ORCAD_LAUNCHER_ENTRY_POINT)],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
target: 'node18',
|
||||
target: ORCAD_COMPAT_BUILD_TARGET,
|
||||
format: 'cjs',
|
||||
outfile,
|
||||
metafile: true,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { build } from 'esbuild'
|
||||
import { join } from 'node:path'
|
||||
import { ORCAD_LINEAR_SDK_ENTRY } from '../../src/shared/orcad-artifacts.ts'
|
||||
import { ORCAD_SERVER_BUILD_TARGET } from './orcad-entry-build.mjs'
|
||||
|
||||
// Bundle @linear/sdk into one CJS file for lazy require: copying would expose chunk names that change each release.
|
||||
export async function stageOrcadLinearSdk(root, outputDir) {
|
||||
@@ -12,7 +13,7 @@ export async function stageOrcadLinearSdk(root, outputDir) {
|
||||
},
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
target: 'node18',
|
||||
target: ORCAD_SERVER_BUILD_TARGET,
|
||||
format: 'cjs',
|
||||
outfile: join(outputDir, ORCAD_LINEAR_SDK_ENTRY),
|
||||
minify: true,
|
||||
|
||||
@@ -244,21 +244,21 @@ describe('bundled Orca runtime handoff', () => {
|
||||
})
|
||||
|
||||
describe('server runtime admission', () => {
|
||||
it.each(['14.21.3', '16.20.2'])('refuses host Node %s before loading profile state', (node) => {
|
||||
fixture.exists.mockReturnValue(false)
|
||||
vi.spyOn(process, 'versions', 'get').mockReturnValue({ ...process.versions, node })
|
||||
expect(() => assertOrcadServerRuntime()).toThrow('requires Node.js 18')
|
||||
})
|
||||
|
||||
it.each(['18.20.8', '20.19.0', '22.14.0', '23.0.0', '24.0.0', '26.0.0'])(
|
||||
'accepts unpackaged host Node %s',
|
||||
it.each(['14.21.3', '16.20.2', '18.20.8', '20.19.0', '22.14.0', '23.0.0'])(
|
||||
'refuses host Node %s before loading profile state',
|
||||
(node) => {
|
||||
fixture.exists.mockReturnValue(false)
|
||||
vi.spyOn(process, 'versions', 'get').mockReturnValue({ ...process.versions, node })
|
||||
expect(() => assertOrcadServerRuntime()).not.toThrow()
|
||||
expect(() => assertOrcadServerRuntime()).toThrow('requires Node.js 24')
|
||||
}
|
||||
)
|
||||
|
||||
it.each(['24.0.0', '26.0.0'])('accepts unpackaged host Node %s', (node) => {
|
||||
fixture.exists.mockReturnValue(false)
|
||||
vi.spyOn(process, 'versions', 'get').mockReturnValue({ ...process.versions, node })
|
||||
expect(() => assertOrcadServerRuntime()).not.toThrow()
|
||||
})
|
||||
|
||||
it('refuses host Node 24 for a packaged slot that owns a bundled runtime', () => {
|
||||
vi.spyOn(process, 'versions', 'get').mockReturnValue({ ...process.versions, node: '24.0.0' })
|
||||
expect(() => assertOrcadServerRuntime()).toThrow('Start the Orca server with its bundled')
|
||||
|
||||
@@ -61,8 +61,8 @@ export function isRunningAsBundledOrcadRuntime(directory: string): boolean {
|
||||
|
||||
/** Reject substituted runtimes before opening profile state. */
|
||||
export function assertOrcadServerRuntime(): void {
|
||||
if (Number(process.versions.node.split('.')[0]) < 18) {
|
||||
throw new OrcadBundledRuntimeError('The Orca server requires Node.js 18 or newer')
|
||||
if (Number(process.versions.node.split('.')[0]) < 24) {
|
||||
throw new OrcadBundledRuntimeError('The Orca server requires Node.js 24 or newer')
|
||||
}
|
||||
const directory = resolveBundledOrcadSlot()
|
||||
const runtime = resolveBundledOrcadRuntime(directory)
|
||||
|
||||
@@ -89,10 +89,13 @@ it('runs an unpackaged server on the supported host runtime and preserves argume
|
||||
'console.log(JSON.stringify({ node: process.versions.node, args: process.argv.slice(2) }))'
|
||||
)
|
||||
await buildLauncher(entry)
|
||||
const version = await runProcess({ program: launcherNode, args: ['-p', 'process.versions.node'] })
|
||||
const version = await runProcess({
|
||||
program: process.execPath,
|
||||
args: ['-p', 'process.versions.node']
|
||||
})
|
||||
const args = ['a path with spaces', '--port', '0']
|
||||
const result = await runProcess({
|
||||
program: launcherNode,
|
||||
program: process.execPath,
|
||||
args: [entry, ...args],
|
||||
env: { ...process.env, ORCA_BACKGROUND_LAUNCH: '1' }
|
||||
})
|
||||
@@ -100,6 +103,25 @@ it('runs an unpackaged server on the supported host runtime and preserves argume
|
||||
expect(JSON.parse(result.stdout)).toEqual({ node: version.stdout.trim(), args })
|
||||
})
|
||||
|
||||
it('refuses an old host runtime without a bundle before parsing the server', async ({ skip }) => {
|
||||
const version = await runProcess({
|
||||
program: launcherNode,
|
||||
args: ['-p', 'process.versions.node']
|
||||
})
|
||||
if (Number(version.stdout.trim().split('.')[0]) >= 24) {
|
||||
skip()
|
||||
}
|
||||
const result = await runProcess({
|
||||
program: launcherNode,
|
||||
args: [launcher],
|
||||
env: { ...process.env, ORCA_BACKGROUND_LAUNCH: '1' }
|
||||
})
|
||||
expect(result.code).toBe(78)
|
||||
expect(result.stderr).toContain('requires Node.js 24')
|
||||
expect(result.stderr).not.toContain('SyntaxError')
|
||||
expect(result.stdout).toBe('')
|
||||
})
|
||||
|
||||
it('preserves the crash exit code when the server fails while loading', async () => {
|
||||
const directory = join(root, 'server-crash')
|
||||
await mkdir(directory)
|
||||
@@ -159,23 +181,4 @@ describe.skipIf(skip)('split launcher with the real bundled runtime', () => {
|
||||
expect(result.stderr).toContain('server does not match its launcher')
|
||||
expect(result.stderr).not.toContain('server was loaded')
|
||||
})
|
||||
|
||||
it('refuses an old host runtime without a bundle before loading the server', async ({ skip }) => {
|
||||
const version = await runProcess({
|
||||
program: launcherNode,
|
||||
args: ['-p', 'process.versions.node']
|
||||
})
|
||||
if (Number(version.stdout.trim().split('.')[0]) >= 18) {
|
||||
skip()
|
||||
}
|
||||
await writeFile(join(root, ORCAD_SERVER_ENTRY_FILENAME), 'throw new Error("server was loaded")')
|
||||
const result = await runProcess({
|
||||
program: launcherNode,
|
||||
args: [launcher],
|
||||
env: { ...process.env, ORCA_BACKGROUND_LAUNCH: '1' }
|
||||
})
|
||||
expect(result.code).toBe(78)
|
||||
expect(result.stderr).toContain('requires Node.js 18')
|
||||
expect(result.stderr).not.toContain('server was loaded')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -26,7 +26,7 @@ function buildOrcadBundles(): string[] {
|
||||
const out = ${JSON.stringify(directory)}
|
||||
await entries.buildOrcadEntry(join(out, 'orcad.js'))
|
||||
await Promise.all(Object.values(entries.ORCAD_CHILD_ENTRY_POINTS).map((entry) => build({
|
||||
entryPoints: [entry], bundle: true, platform: 'node', target: 'node18', format: 'cjs',
|
||||
entryPoints: [entry], bundle: true, platform: 'node', target: entries.ORCAD_SERVER_BUILD_TARGET, format: 'cjs',
|
||||
outfile: join(out, entries.orcadChildOutputFilename(entry)),
|
||||
external: entries.ORCAD_EXTERNAL_MODULES, plugins: [entries.externalNativeAddons],
|
||||
logLevel: 'error'
|
||||
|
||||
Reference in New Issue
Block a user