mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 11:25:50 -06:00
perf(ci): content-hash boundary artifact freshness and resize Telegram shards (#123649)
Hosted CI runners restored the boundary-artifact cache and rebuilt it anyway: fresh checkouts re-stamp every input mtime, so mtime freshness never passed. Stamp files now record the input content digest and byte-identical inputs skip the rebuild (~60s saved per hosted lint/boundary job, 0.17s verify). Telegram CI shards pack ten files per job instead of five now that per-file import cost is back to seconds (#123607), halving the ~42-job fanout.
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
// Prepare Extension Package Boundary Artifacts tests cover prepare extension package boundary artifacts script behavior.
|
||||
import { spawn } from "node:child_process";
|
||||
// Prepare Extension Package Boundary Artifacts tests cover prepare extension package boundary artifacts script behavior.
|
||||
import { EventEmitter } from "node:events";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
@@ -15,6 +14,7 @@ import {
|
||||
} from "../../scripts/lib/plugin-sdk-entries.mjs";
|
||||
import { resolveWindowsTaskkillPath } from "../../scripts/lib/windows-taskkill.mjs";
|
||||
import {
|
||||
computeArtifactInputsDigest,
|
||||
createPrefixedOutputWriter,
|
||||
isArtifactSetFresh,
|
||||
parseMode,
|
||||
@@ -562,6 +562,48 @@ describe("prepare-extension-package-boundary-artifacts", () => {
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps mtime-stale artifacts fresh when the hash stamp matches the input digest", () => {
|
||||
// Regression: fresh checkouts re-stamp every input mtime, so cache-restored
|
||||
// artifacts must stay fresh by content identity, not build again per CI run.
|
||||
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-boundary-hash-"));
|
||||
tempRoots.add(rootDir);
|
||||
const inputPath = path.join(rootDir, "src", "demo.ts");
|
||||
const stampPath = path.join(rootDir, "dist", ".demo.stamp");
|
||||
const outputPath = path.join(rootDir, "dist", "demo.d.ts");
|
||||
fs.mkdirSync(path.dirname(inputPath), { recursive: true });
|
||||
fs.mkdirSync(path.dirname(stampPath), { recursive: true });
|
||||
fs.writeFileSync(inputPath, "export const demo = 1;\n", "utf8");
|
||||
fs.writeFileSync(outputPath, "export declare const demo = 1;\n", "utf8");
|
||||
fs.writeFileSync(
|
||||
stampPath,
|
||||
`${computeArtifactInputsDigest({ rootDir, inputPaths: ["src"] })}\n`,
|
||||
"utf8",
|
||||
);
|
||||
|
||||
// Simulate checkout: inputs newer than restored outputs, bytes unchanged.
|
||||
fs.utimesSync(stampPath, new Date(1_000), new Date(1_000));
|
||||
fs.utimesSync(outputPath, new Date(1_000), new Date(1_000));
|
||||
const freshParams = {
|
||||
rootDir,
|
||||
inputPaths: ["src"],
|
||||
outputPaths: ["dist/.demo.stamp", "dist/demo.d.ts"],
|
||||
hashStampPath: "dist/.demo.stamp",
|
||||
};
|
||||
|
||||
expect(isArtifactSetFresh(freshParams)).toBe(true);
|
||||
// The hash match repairs output mtimes so the next check takes the fast path.
|
||||
expect(fs.statSync(outputPath).mtimeMs).toBeGreaterThanOrEqual(fs.statSync(inputPath).mtimeMs);
|
||||
|
||||
fs.appendFileSync(inputPath, "export const demoTwo = 2;\n", "utf8");
|
||||
fs.utimesSync(outputPath, new Date(1_000), new Date(1_000));
|
||||
expect(isArtifactSetFresh(freshParams)).toBe(false);
|
||||
|
||||
// Legacy timestamp stamps never satisfy the hash fallback.
|
||||
fs.writeFileSync(stampPath, `${new Date(5_000).toISOString()}\n`, "utf8");
|
||||
fs.utimesSync(stampPath, new Date(1_000), new Date(1_000));
|
||||
expect(isArtifactSetFresh(freshParams)).toBe(false);
|
||||
});
|
||||
|
||||
it("requires generated entry-shim outputs in addition to the freshness stamp", () => {
|
||||
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-boundary-entry-shims-"));
|
||||
tempRoots.add(rootDir);
|
||||
|
||||
Reference in New Issue
Block a user