singularity-forge/scripts/ensure-source-resources.cjs

161 lines
4.3 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
const { spawnSync } = require("node:child_process");
const { existsSync, readdirSync, statSync } = require("node:fs");
const { join, resolve } = require("node:path");
const root = resolve(__dirname, "..");
const srcResources = join(root, "src", "resources");
const distResources = join(root, "dist", "resources");
const stampPath = join(distResources, ".sf-resource-build-stamp");
const copyResourcesScript = join(root, "scripts", "copy-resources.cjs");
function latestMtimeMs(path) {
let latest = 0;
const stack = [path];
while (stack.length > 0) {
const current = stack.pop();
if (!current) continue;
let entries;
try {
entries = readdirSync(current, { withFileTypes: true });
} catch {
continue;
}
for (const entry of entries) {
const entryPath = join(current, entry.name);
let stat;
try {
stat = statSync(entryPath);
} catch {
continue;
}
latest = Math.max(latest, stat.mtimeMs);
if (entry.isDirectory()) {
stack.push(entryPath);
}
}
}
return latest;
}
function sourceInputsMtimeMs() {
return Math.max(
latestMtimeMs(srcResources),
existsSync(copyResourcesScript) ? statSync(copyResourcesScript).mtimeMs : 0,
existsSync(join(root, "tsconfig.resources.json"))
? statSync(join(root, "tsconfig.resources.json")).mtimeMs
: 0,
);
}
function hasCompleteResourceBuild() {
return (
existsSync(stampPath) &&
existsSync(join(distResources, "SF-WORKFLOW.md")) &&
existsSync(join(distResources, "agents")) &&
existsSync(join(distResources, "extensions"))
);
}
function shouldRebuild() {
if (process.env.SF_DEV_CLI_SKIP_RESOURCE_BUILD === "1") return false;
if (process.env.SF_SKIP_SOURCE_RESOURCE_BUILD === "1") return false;
if (!hasCompleteResourceBuild()) return true;
let stampMtime = 0;
try {
stampMtime = statSync(stampPath).mtimeMs;
} catch {
return true;
}
return sourceInputsMtimeMs() > stampMtime;
}
if (shouldRebuild()) {
console.error(
"[forge] Source resources changed; rebuilding dist/resources before launch...",
);
const result = spawnSync(process.execPath, [copyResourcesScript], {
cwd: root,
stdio: "inherit",
env: process.env,
});
if (result.status !== 0) {
process.exit(result.status ?? 1);
}
}
/**
* Auto-rebuild workspace packages whose src/ is newer than dist/.
*
* Without this, edits to packages/coding-agent/src/* (or any other workspace
* package src) silently land while the dist stays stale agents continue
* loading the old compiled JS and operators see "why didn't my edit take
* effect?" symptoms (observed 2026-05-17 with the AST tools wire-in).
*
* Skipped:
* - native: Rust build takes 510 min; trigger manually via
* `node rust-engine/scripts/build.js --dev` instead.
* - any package set in SF_SKIP_WORKSPACE_AUTOBUILD (comma-separated names).
*
* Override the whole step with SF_SKIP_WORKSPACE_AUTOBUILD=all.
*/
const WORKSPACE_AUTOBUILD_PACKAGES = [
"agent-core",
"ai",
"coding-agent",
"daemon",
"google-gemini-cli-provider",
"openai-codex-provider",
"rpc-client",
"tui",
];
function packageNeedsRebuild(pkgName) {
const pkgDir = join(root, "packages", pkgName);
const pkgSrc = join(pkgDir, "src");
const pkgDist = join(pkgDir, "dist");
if (!existsSync(pkgSrc) || !existsSync(pkgDist)) return false;
const srcMtime = latestMtimeMs(pkgSrc);
const distMtime = latestMtimeMs(pkgDist);
// Add a small grace window so files written within the same second as a
// dist sync don't trigger redundant rebuilds.
return srcMtime > distMtime + 100;
}
function rebuildWorkspacePackagesIfStale() {
const skip = (process.env.SF_SKIP_WORKSPACE_AUTOBUILD || "")
.split(",")
.map((s) => s.trim())
.filter(Boolean);
if (skip.includes("all")) return;
const stale = WORKSPACE_AUTOBUILD_PACKAGES.filter(
(pkg) => !skip.includes(pkg) && packageNeedsRebuild(pkg),
);
if (stale.length === 0) return;
console.error(
`[forge] Workspace src newer than dist for: ${stale.join(", ")} — rebuilding...`,
);
for (const pkg of stale) {
const result = spawnSync(
"npm",
["run", "build", "-w", `@singularity-forge/${pkg}`],
{ cwd: root, stdio: "inherit", env: process.env },
);
if (result.status !== 0) {
console.error(`[forge] FAILED to rebuild packages/${pkg}`);
process.exit(result.status ?? 1);
}
}
}
rebuildWorkspacePackagesIfStale();