build(linux): carry the pinned Node inside the AppImage so serve's slot works offline (#28204)

* build(linux): carry the pinned Node inside the AppImage so orca serve's slot works offline

Linux packaging places the pinned glibc Node at resources/node-runtime/bin/node before the
glibc-floor gate, proves it serves one PTY with the packaged daemon entry, and deb/rpm exclude
it through fpm. orca serve's orcad slot copies that Node (hash-checked) instead of downloading.

* test(serve): type the bundled Node fixture

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
This commit is contained in:
OrcaWin
2026-10-13 00:39:39 -07:00
committed by GitHub
co-authored by m4air
parent e9285f0f58
commit 49a1129aa6
10 changed files with 353 additions and 10 deletions
@@ -0,0 +1,42 @@
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { NODE_RUNTIME_ASSETS } from '../../shared/node-runtime-pin'
import { findAppImageNodeRuntime } from './appimage-bundled-node-runtime'
let resources = ''
beforeEach(() => {
resources = mkdtempSync(join(tmpdir(), 'orca-appimage-node-'))
})
afterEach(() => {
rmSync(resources, { recursive: true, force: true })
})
function bundle(): string {
const path = join(resources, 'node-runtime', 'bin', 'node')
mkdirSync(join(path, '..'), { recursive: true })
writeFileSync(path, '')
return path
}
describe('findAppImageNodeRuntime', () => {
it('names the pinned glibc Node a Linux package carries for its CPU', () => {
const path = bundle()
expect(findAppImageNodeRuntime(resources, 'arm64', 'linux')).toEqual({
path,
target: 'linux-arm64-glibc',
executableSha256: NODE_RUNTIME_ASSETS['linux-arm64-glibc'].executableSha256
})
})
it('finds nothing in a package without one, or off Linux', () => {
expect(findAppImageNodeRuntime(resources, 'x64', 'linux')).toBeNull()
bundle()
expect(findAppImageNodeRuntime(resources, 'x64', 'darwin')).toBeNull()
expect(findAppImageNodeRuntime(resources, 'ia32', 'linux')).toBeNull()
expect(findAppImageNodeRuntime(undefined, 'x64', 'linux')).toBeNull()
})
})
@@ -0,0 +1,50 @@
/** The pinned Node inside a Linux AppImage's resources, used where Orca would otherwise download it. */
import { randomUUID } from 'node:crypto'
import { existsSync } from 'node:fs'
import { chmod, copyFile, mkdir, rename, rm } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import {
APPIMAGE_NODE_RUNTIME_SEGMENTS,
appImageNodeRuntimeTarget
} from '../../shared/appimage-node-runtime'
import { NODE_RUNTIME_ASSETS, type ServerTarget } from '../../shared/node-runtime-pin'
import { verifyFileSha256 } from './pinned-runtime-materializer'
export type AppImageNodeRuntime = {
path: string
target: ServerTarget
executableSha256: string
}
/** Null for every package that carries none: deb, rpm, macOS, Windows and dev builds. */
export function findAppImageNodeRuntime(
resourcesPath: string | undefined,
arch: string = process.arch,
platform: NodeJS.Platform = process.platform
): AppImageNodeRuntime | null {
const target = appImageNodeRuntimeTarget(arch)
if (platform !== 'linux' || !resourcesPath || !target) {
return null
}
const path = join(resourcesPath, ...APPIMAGE_NODE_RUNTIME_SEGMENTS)
return existsSync(path)
? { path, target, executableSha256: NODE_RUNTIME_ASSETS[target].executableSha256 }
: null
}
/** Copies the bundled Node to `destination`; only bytes that hash to the pin are published. */
export async function copyVerifiedAppImageNodeRuntime(
runtime: AppImageNodeRuntime,
destination: string
): Promise<void> {
await mkdir(dirname(destination), { recursive: true })
const staging = `${destination}.copy-${process.pid}-${randomUUID()}`
try {
await copyFile(runtime.path, staging)
await verifyFileSha256(staging, runtime.executableSha256, 'bundled Node')
await chmod(staging, 0o755)
await rename(staging, destination)
} finally {
await rm(staging, { force: true })
}
}