chore(harness): run changed suites in CI

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-03-24 18:34:56 +08:00
parent 7b51090823
commit 151d776842
4 changed files with 226 additions and 24 deletions
+69 -15
View File
@@ -89,6 +89,9 @@ const suiteOrder = [
"release"
];
const prSupersededSuites = ["static", "component", "docs", "a11y", "docs-smoke", "consumers"];
const releaseSupersededSuites = [...prSupersededSuites, "pr"];
const rootStaticFiles = new Set([
"eslint.config.mjs",
"package.json",
@@ -101,7 +104,7 @@ const rootStaticFiles = new Set([
]);
function normalizeFilePath(filePath) {
return filePath.split(path.sep).join(path.posix.sep);
return filePath.replace(/\\/g, path.posix.sep).split(path.sep).join(path.posix.sep);
}
function isExactMatch(filePath, exactPaths) {
@@ -147,9 +150,12 @@ function isConsumerSurfaceChange(filePath) {
);
}
function isHarnessCodeChange(filePath) {
function isHarnessControlPlaneChange(filePath) {
return (
isWithin(filePath, "scripts/harness") ||
filePath === "playwright.config.ts" ||
filePath === "vitest.config.ts" ||
isWithin(filePath, "tests/e2e/support") ||
filePath === "AGENTS.md" ||
filePath === "CONTRIBUTING.md" ||
filePath === "README.md" ||
@@ -159,6 +165,22 @@ function isHarnessCodeChange(filePath) {
);
}
function isReleaseRiskChange(filePath) {
return (
filePath === "package.json" ||
filePath === "docs/releasing.md" ||
filePath === "docs/registry.md" ||
filePath === "scripts/release-metadata.mjs" ||
filePath === "scripts/build-registry.mjs" ||
filePath === "scripts/registry-install.mjs" ||
filePath === "packages/ui/package.json" ||
filePath === "packages/tokens/package.json" ||
isWithin(filePath, ".github/workflows") ||
isWithin(filePath, "tests/package-consumer") ||
isWithin(filePath, "tests/registry")
);
}
function isStaticSurfaceChange(filePath) {
return (
isPackageContractChange(filePath) ||
@@ -167,7 +189,8 @@ function isStaticSurfaceChange(filePath) {
isWithin(filePath, "scripts") ||
isWithin(filePath, ".github/workflows") ||
isExactMatch(filePath, rootStaticFiles) ||
isHarnessCodeChange(filePath)
isHarnessControlPlaneChange(filePath) ||
isReleaseRiskChange(filePath)
);
}
@@ -186,17 +209,22 @@ function runGitCommand(args) {
.map((line) => normalizeFilePath(line));
}
function getWorkingTreeChangedFiles() {
function getWorkingTreeChangedFiles(runGitCommandFn = runGitCommand) {
const files = new Set([
...runGitCommand(["diff", "--name-only", "--diff-filter=ACMR", "HEAD"]),
...runGitCommand(["diff", "--cached", "--name-only", "--diff-filter=ACMR", "HEAD"]),
...runGitCommand(["ls-files", "--others", "--exclude-standard"])
...runGitCommandFn(["diff", "--name-only", "--diff-filter=ACMR", "HEAD"]),
...runGitCommandFn(["diff", "--cached", "--name-only", "--diff-filter=ACMR", "HEAD"]),
...runGitCommandFn(["ls-files", "--others", "--exclude-standard"])
]);
return [...files].sort();
}
export function getChangedFiles({ changedFiles = [], from = null, to = null } = {}) {
export function getChangedFiles(options = {}) {
const changedFiles = options.changedFiles ?? [];
const from = options.from ?? null;
const to = options.to ?? null;
const runGitCommandFn = options.runGitCommandFn ?? runGitCommand;
if (changedFiles.length > 0) {
return [...new Set(changedFiles.map((filePath) => normalizeFilePath(filePath)))].sort();
}
@@ -207,8 +235,8 @@ export function getChangedFiles({ changedFiles = [], from = null, to = null } =
if (from && zeroShaPattern.test(from)) {
return [
...new Set([
...runGitCommand(["ls-files"]),
...runGitCommand(["ls-files", "--others", "--exclude-standard"])
...runGitCommandFn(["ls-files"]),
...runGitCommandFn(["ls-files", "--others", "--exclude-standard"])
])
].sort();
}
@@ -217,10 +245,10 @@ export function getChangedFiles({ changedFiles = [], from = null, to = null } =
throw new Error("Expected --from when selecting changed files from git refs.");
}
return runGitCommand(["diff", "--name-only", "--diff-filter=ACMR", `${from}...${toRef}`]);
return [...runGitCommandFn(["diff", "--name-only", "--diff-filter=ACMR", `${from}...${toRef}`])].sort();
}
return getWorkingTreeChangedFiles();
return getWorkingTreeChangedFiles(runGitCommandFn);
}
function addReason(reasonMap, suiteName, filePath, reason) {
@@ -238,6 +266,18 @@ export function selectSuitesForChangedFiles(changedFiles) {
for (const filePath of normalizedFiles) {
let matched = false;
if (isReleaseRiskChange(filePath)) {
selectedSuites.add("release");
addReason(reasons, "release", filePath, "Release or consumer control plane changed.");
matched = true;
}
if (isHarnessControlPlaneChange(filePath)) {
selectedSuites.add("pr");
addReason(reasons, "pr", filePath, "Harness control plane changed.");
matched = true;
}
if (isPackageContractChange(filePath)) {
selectedSuites.add("component");
selectedSuites.add("docs");
@@ -294,6 +334,20 @@ export function selectSuitesForChangedFiles(changedFiles) {
reasons.delete("static");
}
if (selectedSuites.has("pr")) {
for (const suiteName of prSupersededSuites) {
selectedSuites.delete(suiteName);
reasons.delete(suiteName);
}
}
if (selectedSuites.has("release")) {
for (const suiteName of releaseSupersededSuites) {
selectedSuites.delete(suiteName);
reasons.delete(suiteName);
}
}
if (selectedSuites.size === 0 && unmatchedFiles.some((filePath) => isLikelyCodeChange(filePath))) {
selectedSuites.add("static");
@@ -354,9 +408,9 @@ export function ensureKnownSuite(name) {
}
}
export function writeHarnessArtifact(name, payload) {
fs.mkdirSync(artifactsDir, { recursive: true });
const outputPath = path.join(artifactsDir, `${name}.json`);
export function writeHarnessArtifact(name, payload, outputDir = artifactsDir) {
fs.mkdirSync(outputDir, { recursive: true });
const outputPath = path.join(outputDir, `${name}.json`);
fs.writeFileSync(outputPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
return outputPath;
}