Files
tutti/tools/scripts/run-check-changed-cache.mjs
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

145 lines
3.9 KiB
JavaScript

import { spawnSync } from "node:child_process";
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
import { join } from "node:path";
export const laneFingerprintVersion = 1;
export const gitFingerprintMaxBuffer = 128 * 1024 * 1024;
export class LaneCacheError extends Error {}
export function resolveRetryPushReady(requestedPushReady, previousSummary) {
return requestedPushReady || previousSummary?.pushReady === true;
}
export function buildLaneInputFingerprint(input) {
const root = input.workspaceRoot;
const inputFiles = Array.from(new Set(input.lane.inputFiles)).sort();
const hash = createHash("sha256");
const addGitOutput = (label, args) => {
const result = spawnSync("git", args, {
cwd: root,
encoding: null,
maxBuffer: gitFingerprintMaxBuffer
});
if (result.status !== 0) {
throw new Error(
`check:changed failed to fingerprint git ${args.join(" ")}`
);
}
hash.update(label);
hash.update(result.stdout);
};
hash.update(`version:${laneFingerprintVersion}\n`);
hash.update(`base-ref:${input.baseRef}\n`);
hash.update(`key:${input.lane.key}\n`);
hash.update(`label:${input.lane.label}\n`);
hash.update(`command:${JSON.stringify(input.lane.command)}\n`);
hash.update(`input-files:${JSON.stringify(inputFiles)}\n`);
if (inputFiles.length === 0) {
return hash.digest("hex");
}
const pathspec = ["--", ...inputFiles];
addGitOutput("base-diff", [
"diff",
"--binary",
`${input.baseRef}...HEAD`,
...pathspec
]);
addGitOutput("staged-diff", [
"diff",
"--cached",
"--binary",
"HEAD",
...pathspec
]);
addGitOutput("working-diff", ["diff", "--binary", ...pathspec]);
const untrackedResult = spawnSync(
"git",
["ls-files", "--others", "--exclude-standard", "-z", "--", ...inputFiles],
{
cwd: root,
encoding: "utf8",
maxBuffer: gitFingerprintMaxBuffer
}
);
if (untrackedResult.status !== 0) {
throw new Error("check:changed failed to fingerprint untracked files");
}
for (const file of untrackedResult.stdout
.split("\0")
.filter(Boolean)
.sort()) {
hash.update(`untracked:${file}\n`);
hash.update(readFileSync(join(root, file)));
}
return hash.digest("hex");
}
export function selectFailedOnlyLanes(currentLanes, summary) {
if (summary.laneFingerprintVersion !== laneFingerprintVersion) {
throw new LaneCacheError(
"cannot reuse legacy failed-lane state; run pnpm check:changed"
);
}
const previousByKey = new Map(
(summary.results ?? []).map((result) => [result.key, result])
);
const lanesToRun = [];
const reusedResults = [];
for (const lane of currentLanes) {
const previous = previousByKey.get(lane.key);
if (
previous?.exitCode === 0 &&
previous.inputFingerprint === lane.inputFingerprint
) {
reusedResults.push({
...previous,
command: lane.command,
inputFiles: lane.inputFiles,
inputFingerprint: lane.inputFingerprint,
key: lane.key,
label: lane.label,
reused: true
});
} else {
lanesToRun.push(lane);
}
}
return { lanesToRun, reusedResults };
}
export function mergeLaneResults(currentLanes, executedResults, reusedResults) {
const executedByKey = new Map(
executedResults.map((result) => [result.key, result])
);
const reusedByKey = new Map(
reusedResults.map((result) => [result.key, result])
);
return currentLanes.map((lane, index) => {
const result = executedByKey.get(lane.key) ?? reusedByKey.get(lane.key);
if (!result) {
throw new Error(`check:changed missing lane result for ${lane.key}`);
}
return {
...result,
command: lane.command,
index,
inputFiles: lane.inputFiles,
inputFingerprint: lane.inputFingerprint,
key: lane.key,
label: lane.label,
reused: reusedByKey.has(lane.key)
};
});
}