Files
peardata/scripts/make.cjs
T
Raven Scott 726dbdfd56
CI / test (push) Successful in 2m37s
Release rolling / release (push) Successful in 11m29s
Updates
2026-07-30 15:45:33 -04:00

218 lines
6.7 KiB
JavaScript

#!/usr/bin/env node
/**
* Orchestrate peardata binary builds.
*
* node scripts/make.cjs server # Linux server hosts (bare-build)
* node scripts/make.cjs client # all client hosts (electron-forge)
* node scripts/make.cjs both|all # server + client
*
* Env:
* PEARDATA_SERVER_HOSTS=linux-x64,linux-arm64
* PEARDATA_CLIENT_HOSTS=linux-x64,darwin-arm64,…
* PEARDATA_CLIENT_TIMEOUT_MS=600000 (raise when packaging QVAC natives)
* PEARDATA_SKIP_ELECTRON_PREDOWNLOAD=1
* PEARDATA_SKIP_QVAC=1 tools-only Electron client (no @qvac/sdk bundle)
*/
'use strict'
const path = require('path')
const { spawnSync } = require('child_process')
const {
SERVER_LINUX,
ALL_64,
parseHostList,
clientNpmScript,
hostToElectron,
hostSupportsQvacNative,
} = require('./hosts.cjs')
const root = path.resolve(__dirname, '..')
const SERVER_HOSTS = parseHostList(process.env.PEARDATA_SERVER_HOSTS, SERVER_LINUX)
const CLIENT_HOSTS = parseHostList(process.env.PEARDATA_CLIENT_HOSTS, ALL_64)
// QVAC native bundle + forge package is heavier than the old asar-only client
const CLIENT_TIMEOUT_MS = Number(
process.env.PEARDATA_CLIENT_TIMEOUT_MS ||
(process.env.PEARDATA_SKIP_QVAC === '1' ? 600_000 : 1_200_000)
)
function run(cmd, args, opts = {}) {
console.log(`\n$ ${cmd} ${args.join(' ')}\n`)
const t0 = Date.now()
const res = spawnSync(cmd, args, {
cwd: root,
stdio: 'inherit',
env: process.env,
shell: process.platform === 'win32',
...opts,
})
if (res.error) throw res.error
if (res.status !== 0) process.exit(res.status || 1)
console.log(`[make] ok in ${((Date.now() - t0) / 1000).toFixed(1)}s`)
}
function npmRun(script, env) {
run(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', script], {
env: env ? { ...process.env, ...env } : process.env,
})
}
function makeServer(hosts = SERVER_HOSTS) {
for (const host of hosts) {
if (!SERVER_LINUX.includes(host)) {
console.error(`[make] refusing non-Linux server host: ${host}`)
process.exit(1)
}
}
console.log(`[make] server hosts (Linux only): ${hosts.join(', ')}`)
for (const host of hosts) {
run(process.execPath, [
path.join(root, 'scripts', 'bare-standalone.cjs'),
'--product',
'server',
'--host',
host,
])
}
}
function predownloadElectron(hosts) {
if (process.env.PEARDATA_SKIP_ELECTRON_PREDOWNLOAD === '1') {
console.log('[make] skip electron predownload (PEARDATA_SKIP_ELECTRON_PREDOWNLOAD=1)')
return
}
console.log('[make] pre-downloading Electron for client hosts…')
run(process.execPath, [path.join(root, 'scripts', 'predownload-electron.cjs')], {
env: {
...process.env,
PEARDATA_CLIENT_HOSTS: hosts.join(','),
},
})
}
function ensureBareRuntimes(hosts) {
if (process.env.PEARDATA_SKIP_QVAC === '1' || process.env.PEARDATA_SKIP_QVAC === 'true') {
console.log('[make] skip bare-runtime ensure (PEARDATA_SKIP_QVAC=1)')
return
}
// Only QVAC-native hosts need the platform bare binary in the client package
const need = hosts.filter((h) => hostSupportsQvacNative(h))
if (!need.length) return
console.log(`[make] ensuring bare-runtime platform packages: ${need.join(', ')}`)
run(process.execPath, [
path.join(root, 'scripts', 'ensure-qvac-bare-runtimes.cjs'),
'--hosts',
need.join(','),
])
}
function makeClient(hosts = CLIENT_HOSTS) {
console.log(`[make] client hosts: ${hosts.join(', ')}`)
npmRun('build:client-bundle')
predownloadElectron(hosts)
// Cross-host CI: npm ci never installs darwin/win32 optional bare-runtime-* packages.
// Install them before forge packages so the app ships the Bare worker binary.
ensureBareRuntimes(hosts)
for (const host of hosts) {
const script = clientNpmScript(host)
if (!script) {
console.error(`[make] no client script for host ${host}`)
process.exit(1)
}
const { platform, arch } = hostToElectron(host)
// QVAC Bare prebuilds are not published for every Electron host (e.g. no win32-arm64).
// Package a tools-only client there so the matrix still ships a binary.
const forceSkipQvac =
process.env.PEARDATA_SKIP_QVAC === '1' || !hostSupportsQvacNative(host)
if (forceSkipQvac && process.env.PEARDATA_SKIP_QVAC !== '1') {
console.log(
`[make] client ${host}: no @qvac/llm-llamacpp prebuild — PEARDATA_SKIP_QVAC=1 (tools-only)`
)
}
console.log(
`[make] client ${host} (filter prebuilds to ${platform}-${arch}, qvac=${forceSkipQvac ? 'off' : 'on'})`
)
const env = {
...process.env,
npm_config_build_from_source: 'false',
PEARDATA_SKIP_REBUILD: process.env.PEARDATA_SKIP_REBUILD || '1',
PEARDATA_PACKAGE_PLATFORM: platform,
PEARDATA_PACKAGE_ARCH: arch,
PEARDATA_SKIP_PREPACKAGE_BUNDLE: '1',
PEARDATA_SKIP_QVAC: forceSkipQvac ? '1' : process.env.PEARDATA_SKIP_QVAC || '0',
DEBUG:
process.env.DEBUG ||
(process.env.CI ? 'electron-packager,electron-forge:lifecycle' : ''),
}
console.log(
`[make] client timeout: ${(CLIENT_TIMEOUT_MS / 1000).toFixed(0)}s (PEARDATA_CLIENT_TIMEOUT_MS)`
)
const t0 = Date.now()
const res = spawnSync(
process.platform === 'win32' ? 'npm.cmd' : 'npm',
['run', script],
{
cwd: root,
stdio: 'inherit',
env,
shell: process.platform === 'win32',
timeout: CLIENT_TIMEOUT_MS,
killSignal: 'SIGKILL',
}
)
if (res.error) {
if (res.error.code === 'ETIMEDOUT') {
console.error(
`[make] FATAL: client package ${host} exceeded ${CLIENT_TIMEOUT_MS}ms — killed.`
)
process.exit(1)
}
throw res.error
}
if (res.status !== 0) {
console.error(`[make] client package ${host} failed with status ${res.status}`)
process.exit(res.status || 1)
}
console.log(`[make] ok ${host} in ${((Date.now() - t0) / 1000).toFixed(1)}s`)
}
}
function main() {
const mode = process.argv[2] || 'all'
const thisHost = `${process.platform}-${process.arch}`
switch (mode) {
case 'server':
makeServer()
break
case 'client':
makeClient()
break
case 'client-this':
makeClient([thisHost])
break
case 'both':
case 'all':
makeServer()
makeClient()
break
case 'client-all':
makeClient(ALL_64)
break
default:
console.error(`Unknown mode: ${mode}`)
console.error(
'Usage: node scripts/make.cjs [server|client|client-this|both|all|client-all]'
)
console.error(`Server (Linux): ${SERVER_LINUX.join(', ')}`)
console.error(`Client (all): ${ALL_64.join(', ')}`)
process.exit(1)
}
console.log('\n[make] complete')
}
main()