Changes to upstream: - git.js: coordinator commit identity from TD_GIT_AUTHOR_NAME/EMAIL. - build.js: docker runner can share job dirs through a named volume (jobVolume/jobVolumeRoot) for containerized workers. - install.js: staged installer output with logs, start prompt for regions. - web: feed layout, cache-busting asset versions, multi-agent help section. - build-td: fetch checksummed Windows runtime when building on non-Windows. Additions: - deploy/rodecomp: compose stack, Dockerfiles, Caddyfile, server.json, bulk import/verify scripts, and README.md with the deployment notes (the root README stays identical to upstream). - toolchain/msvc-v142: MSVC v142 under Wine (Docker), SDK 10.0.19041.0. - tools/rodecomp: project preparation, Forgejo provisioning, history and identity tools.
88 lines
4.6 KiB
JavaScript
88 lines
4.6 KiB
JavaScript
#!/usr/bin/env node
|
|
// Builds `td` as standalone executables (Node single executable applications): contributors need
|
|
// no Node.js. Output in dist/: td-windows-x64.exe, td-linux-x64, SHA256SUMS.
|
|
//
|
|
// node tools/build-td/build.mjs
|
|
//
|
|
// Needs network access once (esbuild and postject through npx, the Linux Node runtime from
|
|
// nodejs.org, checked against its published SHA256SUMS). Uses the Node runtime that runs this
|
|
// script for Windows, so build with the same Node version as NODE_VERSION.
|
|
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import crypto from 'node:crypto';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
const DIST = path.join(ROOT, 'dist');
|
|
const NODE_VERSION = 'v22.21.0';
|
|
const FUSE = 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2';
|
|
const SCRIPTS = ['ApplySymbols.java', 'DecompileAt.java', 'DisassembleAt.java', 'ExportFunctions.java', 'ExportOriginals.java'];
|
|
const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
const sh = (cmd, args) => execFileSync(cmd, args, { cwd: ROOT, stdio: 'inherit', shell: process.platform === 'win32' });
|
|
const sha256 = (file) => crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
|
|
if (process.version !== NODE_VERSION) throw new Error(`build with Node ${NODE_VERSION} (this is ${process.version})`);
|
|
fs.rmSync(DIST, { recursive: true, force: true });
|
|
fs.mkdirSync(DIST, { recursive: true });
|
|
|
|
// 1. One CommonJS file (single executable applications run a CommonJS script).
|
|
sh(npx, ['--yes', 'esbuild@0.24.0', 'src/client/cli.js', '--bundle', '--platform=node', '--target=node22', '--format=cjs', '--log-level=warning', `--outfile=${path.join(DIST, 'td.cjs')}`]);
|
|
|
|
// 2. The preparation blob: code plus the embedded Ghidra scripts. No code cache or snapshot, so
|
|
// the same blob works for every platform's Node runtime.
|
|
const config = {
|
|
main: path.join(DIST, 'td.cjs'),
|
|
output: path.join(DIST, 'td.blob'),
|
|
disableExperimentalSEAWarning: true,
|
|
useCodeCache: false,
|
|
useSnapshot: false,
|
|
assets: Object.fromEntries(SCRIPTS.map((s) => [s, path.join(ROOT, 'ghidra_scripts', s)])),
|
|
};
|
|
fs.writeFileSync(path.join(DIST, 'sea-config.json'), JSON.stringify(config, null, 2));
|
|
execFileSync(process.execPath, ['--experimental-sea-config', path.join(DIST, 'sea-config.json')], { stdio: 'inherit' });
|
|
|
|
// 3. Runtimes: this Node for Windows (when building on Windows), the official Linux build otherwise.
|
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'td-build-'));
|
|
const targets = [];
|
|
if (process.platform === 'win32') {
|
|
const exe = path.join(DIST, 'td-windows-x64.exe');
|
|
fs.copyFileSync(process.execPath, exe);
|
|
targets.push(exe);
|
|
} else {
|
|
const base = `https://nodejs.org/dist/${NODE_VERSION}`;
|
|
const sums = await (await fetch(`${base}/SHASUMS256.txt`)).text();
|
|
const want = /^([0-9a-f]{64})\s+win-x64\/node\.exe$/m.exec(sums)?.[1];
|
|
if (!want) throw new Error('no checksum for the Windows runtime');
|
|
const exe = path.join(DIST, 'td-windows-x64.exe');
|
|
fs.writeFileSync(exe, Buffer.from(await (await fetch(`${base}/win-x64/node.exe`)).arrayBuffer()));
|
|
if (sha256(exe) !== want) throw new Error('Windows runtime checksum mismatch');
|
|
targets.push(exe);
|
|
}
|
|
{
|
|
const name = `node-${NODE_VERSION}-linux-x64`;
|
|
const base = `https://nodejs.org/dist/${NODE_VERSION}`;
|
|
const sums = await (await fetch(`${base}/SHASUMS256.txt`)).text();
|
|
const want = new RegExp(`^([0-9a-f]{64})\\s+${name}\\.tar\\.gz$`, 'm').exec(sums)?.[1];
|
|
if (!want) throw new Error('no checksum for the Linux runtime');
|
|
const tgz = path.join(tmp, `${name}.tar.gz`);
|
|
fs.writeFileSync(tgz, Buffer.from(await (await fetch(`${base}/${name}.tar.gz`)).arrayBuffer()));
|
|
if (sha256(tgz) !== want) throw new Error('Linux runtime checksum mismatch');
|
|
execFileSync(process.platform === 'win32' ? path.join(process.env.SystemRoot, 'System32', 'tar.exe') : 'tar', ['-xzf', tgz, '-C', tmp, `${name}/bin/node`], { stdio: 'inherit' });
|
|
const bin = path.join(DIST, 'td-linux-x64');
|
|
fs.copyFileSync(path.join(tmp, name, 'bin', 'node'), bin);
|
|
targets.push(bin);
|
|
}
|
|
|
|
// 4. Inject the blob into each runtime.
|
|
for (const t of targets) {
|
|
const args = ['--yes', 'postject@1.0.0-alpha.6', t, 'NODE_SEA_BLOB', config.output, '--sentinel-fuse', FUSE];
|
|
sh(npx, args);
|
|
}
|
|
|
|
fs.writeFileSync(path.join(DIST, 'SHA256SUMS'), targets.map((t) => `${sha256(t)} ${path.basename(t)}`).join('\n') + '\n');
|
|
for (const f of ['td.cjs', 'td.blob', 'sea-config.json']) fs.rmSync(path.join(DIST, f));
|
|
fs.rmSync(tmp, { recursive: true, force: true });
|
|
console.log(fs.readFileSync(path.join(DIST, 'SHA256SUMS'), 'utf8'));
|