mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 17:17:38 +00:00
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:
@@ -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 })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user