CLI: skip runner rebuilds when dist is fresh (#1231)
Co-authored-by: mukhtharcm <mukhtharcm@users.noreply.github.com>
This commit is contained in:
@@ -17,6 +17,7 @@ Docs: https://docs.clawd.bot
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
- Tests: stabilize Windows gateway/CLI tests by skipping sidecars, normalizing argv, and extending timeouts.
|
- Tests: stabilize Windows gateway/CLI tests by skipping sidecars, normalizing argv, and extending timeouts.
|
||||||
|
- CLI: skip runner rebuilds when dist is fresh. (#1231) — thanks @mukhtharcm, @thewilloftheshadow.
|
||||||
|
|
||||||
## 2026.1.19-1
|
## 2026.1.19-1
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,89 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
import { spawn } from "node:child_process";
|
import { spawn } from "node:child_process";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
import process from "node:process";
|
import process from "node:process";
|
||||||
|
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
const env = { ...process.env };
|
const env = { ...process.env };
|
||||||
const cwd = process.cwd();
|
const cwd = process.cwd();
|
||||||
|
|
||||||
const build = spawn("pnpm", ["exec", "tsc", "-p", "tsconfig.json"], {
|
const distRoot = path.join(cwd, "dist");
|
||||||
cwd,
|
const distEntry = path.join(distRoot, "entry.js");
|
||||||
env,
|
const buildStampPath = path.join(distRoot, ".buildstamp");
|
||||||
stdio: "inherit",
|
const srcRoot = path.join(cwd, "src");
|
||||||
});
|
const configFiles = [path.join(cwd, "tsconfig.json"), path.join(cwd, "package.json")];
|
||||||
|
|
||||||
build.on("exit", (code, signal) => {
|
const statMtime = (filePath) => {
|
||||||
if (signal) {
|
try {
|
||||||
process.exit(1);
|
return fs.statSync(filePath).mtimeMs;
|
||||||
return;
|
} 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], {
|
const nodeProcess = spawn(process.execPath, ["dist/entry.js", ...args], {
|
||||||
cwd,
|
cwd,
|
||||||
env,
|
env,
|
||||||
@@ -35,4 +97,39 @@ build.on("exit", (code, signal) => {
|
|||||||
}
|
}
|
||||||
process.exit(exitCode ?? 1);
|
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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -102,8 +102,10 @@ const normalizePluginsConfig = (config?: ClawdbotConfig["plugins"]): NormalizedP
|
|||||||
|
|
||||||
const resolvePluginSdkAlias = (): string | null => {
|
const resolvePluginSdkAlias = (): string | null => {
|
||||||
try {
|
try {
|
||||||
const preferDist = process.env.VITEST || process.env.NODE_ENV === "test";
|
const modulePath = fileURLToPath(import.meta.url);
|
||||||
let cursor = path.dirname(fileURLToPath(import.meta.url));
|
const isDistRuntime = modulePath.split(path.sep).includes("dist");
|
||||||
|
const preferDist = process.env.VITEST || process.env.NODE_ENV === "test" || isDistRuntime;
|
||||||
|
let cursor = path.dirname(modulePath);
|
||||||
for (let i = 0; i < 6; i += 1) {
|
for (let i = 0; i < 6; i += 1) {
|
||||||
const srcCandidate = path.join(cursor, "src", "plugin-sdk", "index.ts");
|
const srcCandidate = path.join(cursor, "src", "plugin-sdk", "index.ts");
|
||||||
const distCandidate = path.join(cursor, "dist", "plugin-sdk", "index.js");
|
const distCandidate = path.join(cursor, "dist", "plugin-sdk", "index.js");
|
||||||
|
|||||||
Reference in New Issue
Block a user