mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-13 15:47:28 +00:00
fix(plugins): track package boundary dts freshness
This commit is contained in:
@@ -193,6 +193,7 @@ function isRelevantCompileInput(filePath) {
|
|||||||
|
|
||||||
function collectNewestMtime(entryPath, params = {}) {
|
function collectNewestMtime(entryPath, params = {}) {
|
||||||
const includeFile = params.includeFile ?? (() => true);
|
const includeFile = params.includeFile ?? (() => true);
|
||||||
|
const skipDistDirectories = params.skipDistDirectories ?? true;
|
||||||
let newestMtimeMs = 0;
|
let newestMtimeMs = 0;
|
||||||
|
|
||||||
function visit(currentPath) {
|
function visit(currentPath) {
|
||||||
@@ -202,7 +203,7 @@ function collectNewestMtime(entryPath, params = {}) {
|
|||||||
const stats = statSync(currentPath);
|
const stats = statSync(currentPath);
|
||||||
if (stats.isDirectory()) {
|
if (stats.isDirectory()) {
|
||||||
const basename = path.basename(currentPath);
|
const basename = path.basename(currentPath);
|
||||||
if (basename === "dist" || basename === "node_modules") {
|
if ((skipDistDirectories && basename === "dist") || basename === "node_modules") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const child of readdirSync(currentPath)) {
|
for (const child of readdirSync(currentPath)) {
|
||||||
@@ -241,10 +242,9 @@ export function isBoundaryCompileFresh(extensionId, params = {}) {
|
|||||||
collectNewestMtime(extensionRoot, { includeFile: isRelevantCompileInput });
|
collectNewestMtime(extensionRoot, { includeFile: isRelevantCompileInput });
|
||||||
const sharedNewestInputMtimeMs =
|
const sharedNewestInputMtimeMs =
|
||||||
params.sharedNewestInputMtimeMs ??
|
params.sharedNewestInputMtimeMs ??
|
||||||
Math.max(
|
collectNewestMtime(resolve(rootDir, "packages/plugin-sdk/dist"), {
|
||||||
collectNewestMtime(resolve(rootDir, "dist/plugin-sdk")),
|
skipDistDirectories: false,
|
||||||
collectNewestMtime(resolve(rootDir, "packages/plugin-sdk/dist")),
|
});
|
||||||
);
|
|
||||||
const newestInputMtimeMs = Math.max(extensionNewestInputMtimeMs, sharedNewestInputMtimeMs);
|
const newestInputMtimeMs = Math.max(extensionNewestInputMtimeMs, sharedNewestInputMtimeMs);
|
||||||
const oldestOutputMtimeMs = collectOldestMtime([
|
const oldestOutputMtimeMs = collectOldestMtime([
|
||||||
resolveBoundaryTsStampPath(extensionId, rootDir),
|
resolveBoundaryTsStampPath(extensionId, rootDir),
|
||||||
@@ -553,13 +553,19 @@ async function runCompileCheck(extensionIds) {
|
|||||||
process.stdout.write(
|
process.stdout.write(
|
||||||
`preparing plugin-sdk boundary artifacts for ${extensionIds.length} plugins\n`,
|
`preparing plugin-sdk boundary artifacts for ${extensionIds.length} plugins\n`,
|
||||||
);
|
);
|
||||||
runNodeStep("plugin-sdk boundary prep", [prepareBoundaryArtifactsBin], 420_000);
|
runNodeStep(
|
||||||
|
"plugin-sdk boundary prep",
|
||||||
|
[prepareBoundaryArtifactsBin, "--mode=package-boundary"],
|
||||||
|
420_000,
|
||||||
|
);
|
||||||
const prepElapsedMs = Date.now() - prepStartedAt;
|
const prepElapsedMs = Date.now() - prepStartedAt;
|
||||||
const concurrency = resolveCompileConcurrency();
|
const concurrency = resolveCompileConcurrency();
|
||||||
const verboseFreshLogs = process.env.OPENCLAW_EXTENSION_BOUNDARY_VERBOSE_FRESH === "1";
|
const verboseFreshLogs = process.env.OPENCLAW_EXTENSION_BOUNDARY_VERBOSE_FRESH === "1";
|
||||||
const sharedNewestInputMtimeMs = Math.max(
|
const sharedNewestInputMtimeMs = collectNewestMtime(
|
||||||
collectNewestMtime(resolve(repoRoot, "dist/plugin-sdk")),
|
resolve(repoRoot, "packages/plugin-sdk/dist"),
|
||||||
collectNewestMtime(resolve(repoRoot, "packages/plugin-sdk/dist")),
|
{
|
||||||
|
skipDistDirectories: false,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
process.stdout.write(`compile concurrency ${concurrency}\n`);
|
process.stdout.write(`compile concurrency ${concurrency}\n`);
|
||||||
const compileStartedAt = Date.now();
|
const compileStartedAt = Date.now();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const require = createRequire(import.meta.url);
|
|||||||
const repoRoot = resolve(import.meta.dirname, "..");
|
const repoRoot = resolve(import.meta.dirname, "..");
|
||||||
const tscBin = require.resolve("typescript/bin/tsc");
|
const tscBin = require.resolve("typescript/bin/tsc");
|
||||||
const TYPE_INPUT_EXTENSIONS = new Set([".ts", ".tsx", ".d.ts", ".js", ".mjs", ".json"]);
|
const TYPE_INPUT_EXTENSIONS = new Set([".ts", ".tsx", ".d.ts", ".js", ".mjs", ".json"]);
|
||||||
|
const VALID_MODES = new Set(["all", "package-boundary"]);
|
||||||
|
|
||||||
const ROOT_DTS_INPUTS = [
|
const ROOT_DTS_INPUTS = [
|
||||||
"tsconfig.json",
|
"tsconfig.json",
|
||||||
@@ -36,6 +37,15 @@ function isRelevantTypeInput(filePath) {
|
|||||||
return TYPE_INPUT_EXTENSIONS.has(path.extname(filePath));
|
return TYPE_INPUT_EXTENSIONS.has(path.extname(filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parseMode(argv = process.argv.slice(2)) {
|
||||||
|
const modeArg = argv.find((arg) => arg.startsWith("--mode="));
|
||||||
|
const mode = modeArg?.slice("--mode=".length) ?? "all";
|
||||||
|
if (!VALID_MODES.has(mode)) {
|
||||||
|
throw new Error(`Unknown mode: ${mode}`);
|
||||||
|
}
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
function collectNewestMtime(paths, params = {}) {
|
function collectNewestMtime(paths, params = {}) {
|
||||||
const rootDir = params.rootDir ?? repoRoot;
|
const rootDir = params.rootDir ?? repoRoot;
|
||||||
const includeFile = params.includeFile ?? (() => true);
|
const includeFile = params.includeFile ?? (() => true);
|
||||||
@@ -199,8 +209,9 @@ export async function runNodeStepsInParallel(steps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function main() {
|
export async function main(argv = process.argv.slice(2)) {
|
||||||
try {
|
try {
|
||||||
|
const mode = parseMode(argv);
|
||||||
const rootDtsFresh = isArtifactSetFresh({
|
const rootDtsFresh = isArtifactSetFresh({
|
||||||
inputPaths: ROOT_DTS_INPUTS,
|
inputPaths: ROOT_DTS_INPUTS,
|
||||||
outputPaths: ["dist/plugin-sdk/.tsbuildinfo"],
|
outputPaths: ["dist/plugin-sdk/.tsbuildinfo"],
|
||||||
@@ -221,14 +232,16 @@ export async function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const pendingSteps = [];
|
const pendingSteps = [];
|
||||||
if (!rootDtsFresh) {
|
if (mode === "all") {
|
||||||
pendingSteps.push({
|
if (!rootDtsFresh) {
|
||||||
label: "plugin-sdk boundary dts",
|
pendingSteps.push({
|
||||||
args: [tscBin, "-p", "tsconfig.plugin-sdk.dts.json"],
|
label: "plugin-sdk boundary dts",
|
||||||
timeoutMs: 300_000,
|
args: [tscBin, "-p", "tsconfig.plugin-sdk.dts.json"],
|
||||||
});
|
timeoutMs: 300_000,
|
||||||
} else {
|
});
|
||||||
process.stdout.write("[plugin-sdk boundary dts] fresh; skipping\n");
|
} else {
|
||||||
|
process.stdout.write("[plugin-sdk boundary dts] fresh; skipping\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!packageDtsFresh) {
|
if (!packageDtsFresh) {
|
||||||
pendingSteps.push({
|
pendingSteps.push({
|
||||||
@@ -244,13 +257,13 @@ export async function main() {
|
|||||||
await runNodeStepsInParallel(pendingSteps);
|
await runNodeStepsInParallel(pendingSteps);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!entryShimsFresh || pendingSteps.length > 0) {
|
if (mode === "all" && (!entryShimsFresh || pendingSteps.length > 0)) {
|
||||||
await runNodeStep(
|
await runNodeStep(
|
||||||
"plugin-sdk boundary root shims",
|
"plugin-sdk boundary root shims",
|
||||||
["--import", "tsx", resolve(repoRoot, "scripts/write-plugin-sdk-entry-dts.ts")],
|
["--import", "tsx", resolve(repoRoot, "scripts/write-plugin-sdk-entry-dts.ts")],
|
||||||
120_000,
|
120_000,
|
||||||
);
|
);
|
||||||
} else {
|
} else if (mode === "all") {
|
||||||
process.stdout.write("[plugin-sdk boundary root shims] fresh; skipping\n");
|
process.stdout.write("[plugin-sdk boundary root shims] fresh; skipping\n");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -206,30 +206,26 @@ describe("check-extension-package-tsc-boundary", () => {
|
|||||||
).toBe("skipped 97 fresh plugin compiles\n");
|
).toBe("skipped 97 fresh plugin compiles\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("treats a plugin compile as fresh only when its outputs are newer than plugin and sdk inputs", () => {
|
it("treats a plugin compile as fresh only when its outputs are newer than plugin and package sdk inputs", () => {
|
||||||
const { rootDir, extensionRoot } = createTempExtensionRoot();
|
const { rootDir, extensionRoot } = createTempExtensionRoot();
|
||||||
const extensionSourcePath = path.join(extensionRoot, "index.ts");
|
const extensionSourcePath = path.join(extensionRoot, "index.ts");
|
||||||
const extensionTsconfigPath = path.join(extensionRoot, "tsconfig.json");
|
const extensionTsconfigPath = path.join(extensionRoot, "tsconfig.json");
|
||||||
const stampPath = path.join(extensionRoot, "dist", ".boundary-tsc.stamp");
|
const stampPath = path.join(extensionRoot, "dist", ".boundary-tsc.stamp");
|
||||||
const rootSdkBuildInfoPath = path.join(rootDir, "dist", "plugin-sdk", ".tsbuildinfo");
|
const rootSdkTypePath = path.join(rootDir, "dist", "plugin-sdk", "core.d.ts");
|
||||||
const packageSdkBuildInfoPath = path.join(
|
const packageSdkTypePath = path.join(
|
||||||
rootDir,
|
rootDir,
|
||||||
"packages",
|
"packages",
|
||||||
"plugin-sdk",
|
"plugin-sdk",
|
||||||
"dist",
|
"dist",
|
||||||
".tsbuildinfo",
|
"src",
|
||||||
);
|
|
||||||
const entryShimStampPath = path.join(
|
|
||||||
rootDir,
|
|
||||||
"dist",
|
|
||||||
"plugin-sdk",
|
"plugin-sdk",
|
||||||
".boundary-entry-shims.stamp",
|
"core.d.ts",
|
||||||
);
|
);
|
||||||
|
|
||||||
fs.mkdirSync(path.dirname(extensionSourcePath), { recursive: true });
|
fs.mkdirSync(path.dirname(extensionSourcePath), { recursive: true });
|
||||||
fs.mkdirSync(path.dirname(stampPath), { recursive: true });
|
fs.mkdirSync(path.dirname(stampPath), { recursive: true });
|
||||||
fs.mkdirSync(path.dirname(rootSdkBuildInfoPath), { recursive: true });
|
fs.mkdirSync(path.dirname(rootSdkTypePath), { recursive: true });
|
||||||
fs.mkdirSync(path.dirname(packageSdkBuildInfoPath), { recursive: true });
|
fs.mkdirSync(path.dirname(packageSdkTypePath), { recursive: true });
|
||||||
|
|
||||||
fs.writeFileSync(extensionSourcePath, "export const demo = 1;\n", "utf8");
|
fs.writeFileSync(extensionSourcePath, "export const demo = 1;\n", "utf8");
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
@@ -238,26 +234,27 @@ describe("check-extension-package-tsc-boundary", () => {
|
|||||||
"utf8",
|
"utf8",
|
||||||
);
|
);
|
||||||
fs.writeFileSync(stampPath, "ok\n", "utf8");
|
fs.writeFileSync(stampPath, "ok\n", "utf8");
|
||||||
fs.writeFileSync(rootSdkBuildInfoPath, "ok\n", "utf8");
|
fs.writeFileSync(rootSdkTypePath, "export {};\n", "utf8");
|
||||||
fs.writeFileSync(packageSdkBuildInfoPath, "ok\n", "utf8");
|
fs.writeFileSync(packageSdkTypePath, "export {};\n", "utf8");
|
||||||
fs.writeFileSync(entryShimStampPath, "ok\n", "utf8");
|
|
||||||
|
|
||||||
fs.utimesSync(extensionSourcePath, new Date(1_000), new Date(1_000));
|
fs.utimesSync(extensionSourcePath, new Date(1_000), new Date(1_000));
|
||||||
fs.utimesSync(extensionTsconfigPath, new Date(1_000), new Date(1_000));
|
fs.utimesSync(extensionTsconfigPath, new Date(1_000), new Date(1_000));
|
||||||
fs.utimesSync(rootSdkBuildInfoPath, new Date(2_000), new Date(2_000));
|
fs.utimesSync(rootSdkTypePath, new Date(500), new Date(500));
|
||||||
fs.utimesSync(packageSdkBuildInfoPath, new Date(2_000), new Date(2_000));
|
fs.utimesSync(packageSdkTypePath, new Date(2_000), new Date(2_000));
|
||||||
fs.utimesSync(entryShimStampPath, new Date(2_000), new Date(2_000));
|
|
||||||
fs.utimesSync(stampPath, new Date(3_000), new Date(3_000));
|
fs.utimesSync(stampPath, new Date(3_000), new Date(3_000));
|
||||||
|
|
||||||
expect(isBoundaryCompileFresh("demo", { rootDir })).toBe(true);
|
expect(isBoundaryCompileFresh("demo", { rootDir })).toBe(true);
|
||||||
|
|
||||||
fs.utimesSync(rootSdkBuildInfoPath, new Date(500), new Date(500));
|
fs.utimesSync(rootSdkTypePath, new Date(500), new Date(500));
|
||||||
fs.utimesSync(packageSdkBuildInfoPath, new Date(500), new Date(500));
|
fs.utimesSync(packageSdkTypePath, new Date(500), new Date(500));
|
||||||
fs.utimesSync(entryShimStampPath, new Date(500), new Date(500));
|
|
||||||
|
|
||||||
expect(isBoundaryCompileFresh("demo", { rootDir })).toBe(true);
|
expect(isBoundaryCompileFresh("demo", { rootDir })).toBe(true);
|
||||||
|
|
||||||
fs.utimesSync(rootSdkBuildInfoPath, new Date(4_000), new Date(4_000));
|
fs.utimesSync(rootSdkTypePath, new Date(4_000), new Date(4_000));
|
||||||
|
|
||||||
|
expect(isBoundaryCompileFresh("demo", { rootDir })).toBe(true);
|
||||||
|
|
||||||
|
fs.utimesSync(packageSdkTypePath, new Date(4_000), new Date(4_000));
|
||||||
|
|
||||||
expect(isBoundaryCompileFresh("demo", { rootDir })).toBe(false);
|
expect(isBoundaryCompileFresh("demo", { rootDir })).toBe(false);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { afterEach, describe, expect, it } from "vitest";
|
|||||||
import {
|
import {
|
||||||
createPrefixedOutputWriter,
|
createPrefixedOutputWriter,
|
||||||
isArtifactSetFresh,
|
isArtifactSetFresh,
|
||||||
|
parseMode,
|
||||||
runNodeStepsInParallel,
|
runNodeStepsInParallel,
|
||||||
} from "../../scripts/prepare-extension-package-boundary-artifacts.mjs";
|
} from "../../scripts/prepare-extension-package-boundary-artifacts.mjs";
|
||||||
|
|
||||||
@@ -85,4 +86,10 @@ describe("prepare-extension-package-boundary-artifacts", () => {
|
|||||||
}),
|
}),
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("parses prep mode and rejects unknown values", () => {
|
||||||
|
expect(parseMode([])).toBe("all");
|
||||||
|
expect(parseMode(["--mode=package-boundary"])).toBe("package-boundary");
|
||||||
|
expect(() => parseMode(["--mode=nope"])).toThrow("Unknown mode: nope");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user