262 lines
7.2 KiB
JavaScript
262 lines
7.2 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Build peardata-server as a Bare standalone binary (Linux hosts).
|
|
*
|
|
* Flow (Holepunch bare-build + bare-node-runtime):
|
|
* 1. bare-pack the entry with global imports (package.json + bare-node-runtime)
|
|
* 2. Embed the bundle into a bare-runtime prebuild via bare-build platform hooks
|
|
*
|
|
* Usage:
|
|
* node scripts/bare-standalone.cjs --product server --host linux-x64
|
|
* node scripts/bare-standalone.cjs --product server --host all
|
|
*
|
|
* Output:
|
|
* out/peardata-server-<host>/peardata-server
|
|
*/
|
|
'use strict'
|
|
|
|
const path = require('path')
|
|
const fs = require('fs')
|
|
const { pathToFileURL } = require('url')
|
|
const pack = require('bare-pack')
|
|
const { readModule, listPrefix } = require('bare-pack/fs')
|
|
const traverse = require('bare-module-traverse')
|
|
const id = require('bare-bundle-id')
|
|
|
|
const root = path.resolve(__dirname, '..')
|
|
const pkg = require(path.join(root, 'package.json'))
|
|
const { SERVER_LINUX } = require('./hosts.cjs')
|
|
|
|
function parseArgs(argv) {
|
|
const out = {
|
|
product: 'server',
|
|
hosts: [],
|
|
outRoot: path.join(root, 'out'),
|
|
}
|
|
for (let i = 0; i < argv.length; i++) {
|
|
const a = argv[i]
|
|
if (a === '--product') out.product = argv[++i]
|
|
else if (a === '--host') {
|
|
const h = argv[++i]
|
|
if (h === 'all') out.hosts.push(...SERVER_LINUX)
|
|
else out.hosts.push(h)
|
|
} else if (a === '--out') out.outRoot = path.resolve(argv[++i])
|
|
else if (a === '--help' || a === '-h') out.help = true
|
|
}
|
|
if (!out.hosts.length) {
|
|
const thisHost = `${process.platform}-${process.arch}`
|
|
out.hosts.push(SERVER_LINUX.includes(thisHost) ? thisHost : 'linux-x64')
|
|
}
|
|
return out
|
|
}
|
|
|
|
/**
|
|
* Global imports map for bare-pack.
|
|
*/
|
|
function buildImportsMap() {
|
|
let bnr = {}
|
|
try {
|
|
bnr = require('bare-node-runtime/imports')
|
|
} catch {
|
|
console.warn(
|
|
'[bare-standalone] bare-node-runtime/imports not found — relying on package.json imports'
|
|
)
|
|
}
|
|
return { ...bnr, ...(pkg.imports || {}) }
|
|
}
|
|
|
|
function platformForHost(host) {
|
|
const bareBuildRoot = path.dirname(require.resolve('bare-build/package'))
|
|
const load = (name) => require(path.join(bareBuildRoot, 'lib', 'platform', name))
|
|
switch (host) {
|
|
case 'linux-arm64':
|
|
case 'linux-x64':
|
|
return load('linux')
|
|
default:
|
|
throw new Error(
|
|
`peardata-server only builds Linux hosts (got '${host}'). ` +
|
|
`Allowed: ${SERVER_LINUX.join(', ')}`
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} host
|
|
* @param {string} outRoot
|
|
*/
|
|
async function buildOne(host, outRoot) {
|
|
if (!SERVER_LINUX.includes(host)) {
|
|
throw new Error(
|
|
`Refusing non-Linux server host '${host}'. Allowed: ${SERVER_LINUX.join(', ')}`
|
|
)
|
|
}
|
|
|
|
// Ensure native helpers are compiled + base64-embedded into the JS bundle
|
|
console.log('[bare-standalone] building/embedding native helpers…')
|
|
const { spawnSync } = require('child_process')
|
|
const nh = spawnSync(process.execPath, [path.join(root, 'scripts', 'build-native-helpers.cjs')], {
|
|
cwd: root,
|
|
stdio: 'inherit',
|
|
env: process.env,
|
|
})
|
|
if (nh.status !== 0) {
|
|
console.warn('[bare-standalone] native helper build failed (JS fallback still works)')
|
|
}
|
|
|
|
const name = 'peardata-server'
|
|
const outDir = path.join(outRoot, `${name}-${host}`)
|
|
fs.rmSync(outDir, { recursive: true, force: true })
|
|
fs.mkdirSync(outDir, { recursive: true })
|
|
|
|
const entryPath = path.join(root, 'bin', 'peardata-server.mjs')
|
|
if (!fs.existsSync(entryPath)) throw new Error(`Missing entry ${entryPath}`)
|
|
|
|
const imports = buildImportsMap()
|
|
console.log(`[bare-standalone] packing ${name} for ${host}…`)
|
|
|
|
let entry = await pack(
|
|
pathToFileURL(entryPath),
|
|
{
|
|
hosts: [host],
|
|
linked: false,
|
|
resolve: traverse.resolve.bare,
|
|
imports,
|
|
},
|
|
readModule,
|
|
listPrefix
|
|
)
|
|
|
|
const baseURL = pathToFileURL(root + path.sep)
|
|
entry = entry.unmount(baseURL)
|
|
entry.id = id(entry).toString('hex')
|
|
|
|
const platform = platformForHost(host)
|
|
const opts = {
|
|
name,
|
|
version: pkg.version || '0.0.0',
|
|
description: pkg.description || 'peardata server',
|
|
author: pkg.author || '',
|
|
identifier: 'com.peardata.server',
|
|
hosts: [host],
|
|
out: outDir,
|
|
standalone: true,
|
|
package: false,
|
|
base: root,
|
|
}
|
|
|
|
console.log(`[bare-standalone] embedding bare-runtime for ${host}…`)
|
|
for await (const resource of platform(root, entry, null, opts)) {
|
|
if (resource && resource.path) {
|
|
console.log(`[bare-standalone] resource ${resource.path}`)
|
|
}
|
|
}
|
|
|
|
const binName = name
|
|
let binary = path.join(outDir, binName)
|
|
if (!fs.existsSync(binary)) {
|
|
const found = walkFind(outDir, (f) => {
|
|
const base = path.basename(f)
|
|
return base === name || base === 'peardata-server'
|
|
})
|
|
if (found) binary = found
|
|
}
|
|
|
|
if (fs.existsSync(binary)) {
|
|
try {
|
|
fs.chmodSync(binary, 0o755)
|
|
} catch {
|
|
// ignore
|
|
}
|
|
const flat = path.join(outDir, path.basename(binary))
|
|
if (path.resolve(binary) !== path.resolve(flat)) {
|
|
fs.copyFileSync(binary, flat)
|
|
binary = flat
|
|
}
|
|
// Also ship raw helper next to binary for ops that prefer an external file
|
|
const pre = path.join(root, 'native', 'prebuilds', `peardata-ebpf-${host}`)
|
|
const preAlt = path.join(root, 'native', 'prebuilds', 'peardata-ebpf')
|
|
const helperSrc = fs.existsSync(pre) ? pre : fs.existsSync(preAlt) ? preAlt : null
|
|
if (helperSrc) {
|
|
const helperDest = path.join(outDir, 'peardata-ebpf')
|
|
fs.copyFileSync(helperSrc, helperDest)
|
|
try {
|
|
fs.chmodSync(helperDest, 0o755)
|
|
} catch {
|
|
// ignore
|
|
}
|
|
console.log(`[bare-standalone] shipped helper ${helperDest}`)
|
|
}
|
|
console.log(`[bare-standalone] wrote ${binary}`)
|
|
} else {
|
|
console.warn(`[bare-standalone] WARN: expected binary not found under ${outDir}`)
|
|
console.warn(
|
|
' contents:',
|
|
fs.readdirSync(outDir, { recursive: true }).slice(0, 30).join(', ')
|
|
)
|
|
}
|
|
|
|
fs.writeFileSync(
|
|
path.join(outDir, 'build-info.json'),
|
|
JSON.stringify(
|
|
{
|
|
product: 'server',
|
|
host,
|
|
name,
|
|
version: pkg.version,
|
|
builtAt: new Date().toISOString(),
|
|
entry: 'bin/peardata-server.mjs',
|
|
bundleId: entry.id,
|
|
},
|
|
null,
|
|
2
|
|
) + '\n'
|
|
)
|
|
|
|
return outDir
|
|
}
|
|
|
|
function walkFind(dir, pred) {
|
|
const stack = [dir]
|
|
while (stack.length) {
|
|
const d = stack.pop()
|
|
let entries
|
|
try {
|
|
entries = fs.readdirSync(d, { withFileTypes: true })
|
|
} catch {
|
|
continue
|
|
}
|
|
for (const ent of entries) {
|
|
const p = path.join(d, ent.name)
|
|
if (ent.isDirectory()) stack.push(p)
|
|
else if (pred(p)) return p
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
async function main() {
|
|
const opts = parseArgs(process.argv.slice(2))
|
|
if (opts.help) {
|
|
console.log(`Usage: node scripts/bare-standalone.cjs [--product server] [--host <host>|all] [--out dir]
|
|
Server hosts (Linux only): ${SERVER_LINUX.join(', ')}`)
|
|
process.exit(0)
|
|
}
|
|
|
|
if (opts.product !== 'server') {
|
|
throw new Error(
|
|
`bare-standalone only builds product=server. For client use: npm run make:client:<host>`
|
|
)
|
|
}
|
|
|
|
const results = []
|
|
for (const host of opts.hosts) {
|
|
results.push(await buildOne(host, opts.outRoot))
|
|
}
|
|
console.log('[bare-standalone] done:', results.join(', '))
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(err)
|
|
process.exit(1)
|
|
})
|