CLI: skip runner rebuilds when dist is fresh (#1231)
Co-authored-by: mukhtharcm <mukhtharcm@users.noreply.github.com>
This commit is contained in:
@@ -1,27 +1,89 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawn } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const env = { ...process.env };
|
||||
const cwd = process.cwd();
|
||||
|
||||
const build = spawn("pnpm", ["exec", "tsc", "-p", "tsconfig.json"], {
|
||||
cwd,
|
||||
env,
|
||||
stdio: "inherit",
|
||||
});
|
||||
const distRoot = path.join(cwd, "dist");
|
||||
const distEntry = path.join(distRoot, "entry.js");
|
||||
const buildStampPath = path.join(distRoot, ".buildstamp");
|
||||
const srcRoot = path.join(cwd, "src");
|
||||
const configFiles = [path.join(cwd, "tsconfig.json"), path.join(cwd, "package.json")];
|
||||
|
||||
build.on("exit", (code, signal) => {
|
||||
if (signal) {
|
||||
process.exit(1);
|
||||
return;
|
||||
const statMtime = (filePath) => {
|
||||
try {
|
||||
return fs.statSync(filePath).mtimeMs;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
if (code !== 0 && code !== null) {
|
||||
process.exit(code);
|
||||
return;
|
||||
};
|
||||
|
||||
const isExcludedSource = (filePath) => {
|
||||
const relativePath = path.relative(srcRoot, filePath);
|
||||
if (relativePath.startsWith("..")) return false;
|
||||
return (
|
||||
relativePath.endsWith(".test.ts") ||
|
||||
relativePath.endsWith(".test.tsx") ||
|
||||
relativePath.endsWith(`test-helpers.ts`)
|
||||
);
|
||||
};
|
||||
|
||||
const findLatestMtime = (dirPath, shouldSkip) => {
|
||||
let latest = null;
|
||||
const queue = [dirPath];
|
||||
while (queue.length > 0) {
|
||||
const current = queue.pop();
|
||||
if (!current) continue;
|
||||
let entries = [];
|
||||
try {
|
||||
entries = fs.readdirSync(current, { withFileTypes: true });
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(current, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
queue.push(fullPath);
|
||||
continue;
|
||||
}
|
||||
if (!entry.isFile()) continue;
|
||||
if (shouldSkip?.(fullPath)) continue;
|
||||
const mtime = statMtime(fullPath);
|
||||
if (mtime == null) continue;
|
||||
if (latest == null || mtime > latest) {
|
||||
latest = mtime;
|
||||
}
|
||||
}
|
||||
}
|
||||
return latest;
|
||||
};
|
||||
|
||||
const shouldBuild = () => {
|
||||
if (env.CLAWDBOT_FORCE_BUILD === "1") return true;
|
||||
const stampMtime = statMtime(buildStampPath);
|
||||
if (stampMtime == null) return true;
|
||||
if (statMtime(distEntry) == null) return true;
|
||||
|
||||
for (const filePath of configFiles) {
|
||||
const mtime = statMtime(filePath);
|
||||
if (mtime != null && mtime > stampMtime) return true;
|
||||
}
|
||||
|
||||
const srcMtime = findLatestMtime(srcRoot, isExcludedSource);
|
||||
if (srcMtime != null && srcMtime > stampMtime) return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
const logRunner = (message) => {
|
||||
if (env.CLAWDBOT_RUNNER_LOG === "0") return;
|
||||
process.stderr.write(`[clawdbot] ${message}\n`);
|
||||
};
|
||||
|
||||
const runNode = () => {
|
||||
const nodeProcess = spawn(process.execPath, ["dist/entry.js", ...args], {
|
||||
cwd,
|
||||
env,
|
||||
@@ -35,4 +97,39 @@ build.on("exit", (code, signal) => {
|
||||
}
|
||||
process.exit(exitCode ?? 1);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const writeBuildStamp = () => {
|
||||
try {
|
||||
fs.mkdirSync(distRoot, { recursive: true });
|
||||
fs.writeFileSync(buildStampPath, `${Date.now()}\n`);
|
||||
} catch (error) {
|
||||
// Best-effort stamp; still allow the runner to start.
|
||||
logRunner(`Failed to write build stamp: ${error?.message ?? "unknown error"}`);
|
||||
}
|
||||
};
|
||||
|
||||
if (!shouldBuild()) {
|
||||
logRunner("Skipping build; dist is fresh.");
|
||||
runNode();
|
||||
} else {
|
||||
logRunner("Building TypeScript (dist is stale).");
|
||||
const build = spawn("pnpm", ["exec", "tsc", "-p", "tsconfig.json"], {
|
||||
cwd,
|
||||
env,
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
build.on("exit", (code, signal) => {
|
||||
if (signal) {
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
if (code !== 0 && code !== null) {
|
||||
process.exit(code);
|
||||
return;
|
||||
}
|
||||
writeBuildStamp();
|
||||
runNode();
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user