mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(ci): skip Periphery scans for unrelated PR changes (#107991)
* fix(ci): scope Periphery to PR merge changes * test(ci): align Periphery changed-test routing
This commit is contained in:
@@ -26,18 +26,11 @@ jobs:
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
fetch-depth: 2
|
||||
fetch-tags: false
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
- name: Ensure base commit
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
|
||||
uses: ./.github/actions/ensure-base-commit
|
||||
with:
|
||||
base-sha: ${{ github.event.pull_request.base.sha }}
|
||||
fetch-ref: ${{ github.event.pull_request.base.ref }}
|
||||
|
||||
- name: Detect changed paths
|
||||
id: scope
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
@@ -52,14 +45,12 @@ jobs:
|
||||
return;
|
||||
}
|
||||
|
||||
const baseSha = context.payload.pull_request?.base?.sha;
|
||||
if (!baseSha) {
|
||||
throw new Error("missing pull request base SHA");
|
||||
}
|
||||
// pull_request checks out GitHub's synthetic merge commit. Its first
|
||||
// parent is the exact base tree used to produce the tested result.
|
||||
const result = await exec.getExecOutput("git", [
|
||||
"diff",
|
||||
"--quiet",
|
||||
baseSha,
|
||||
"HEAD^1",
|
||||
"HEAD",
|
||||
"--",
|
||||
"apps/ios/",
|
||||
|
||||
@@ -26,18 +26,11 @@ jobs:
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
fetch-depth: 2
|
||||
fetch-tags: false
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
- name: Ensure base commit
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
|
||||
uses: ./.github/actions/ensure-base-commit
|
||||
with:
|
||||
base-sha: ${{ github.event.pull_request.base.sha }}
|
||||
fetch-ref: ${{ github.event.pull_request.base.ref }}
|
||||
|
||||
- name: Detect changed paths
|
||||
id: scope
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
@@ -52,14 +45,12 @@ jobs:
|
||||
return;
|
||||
}
|
||||
|
||||
const baseSha = context.payload.pull_request?.base?.sha;
|
||||
if (!baseSha) {
|
||||
throw new Error("missing pull request base SHA");
|
||||
}
|
||||
// pull_request checks out GitHub's synthetic merge commit. Its first
|
||||
// parent is the exact base tree used to produce the tested result.
|
||||
const result = await exec.getExecOutput("git", [
|
||||
"diff",
|
||||
"--quiet",
|
||||
baseSha,
|
||||
"HEAD^1",
|
||||
"HEAD",
|
||||
"--",
|
||||
"apps/macos/",
|
||||
|
||||
@@ -26,18 +26,11 @@ jobs:
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
fetch-depth: 2
|
||||
fetch-tags: false
|
||||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
- name: Ensure base commit
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
|
||||
uses: ./.github/actions/ensure-base-commit
|
||||
with:
|
||||
base-sha: ${{ github.event.pull_request.base.sha }}
|
||||
fetch-ref: ${{ github.event.pull_request.base.ref }}
|
||||
|
||||
- name: Detect changed paths
|
||||
id: scope
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
@@ -52,14 +45,12 @@ jobs:
|
||||
return;
|
||||
}
|
||||
|
||||
const baseSha = context.payload.pull_request?.base?.sha;
|
||||
if (!baseSha) {
|
||||
throw new Error("missing pull request base SHA");
|
||||
}
|
||||
// pull_request checks out GitHub's synthetic merge commit. Its first
|
||||
// parent is the exact base tree used to produce the tested result.
|
||||
const result = await exec.getExecOutput("git", [
|
||||
"diff",
|
||||
"--quiet",
|
||||
baseSha,
|
||||
"HEAD^1",
|
||||
"HEAD",
|
||||
"--",
|
||||
"apps/ios/",
|
||||
|
||||
@@ -520,14 +520,26 @@ const GITHUB_WORKFLOW_OWNER_TEST_TARGETS = new Map([
|
||||
".github/workflows/ios-periphery-comment.yml",
|
||||
["test/scripts/ios-periphery-comment-workflow.test.ts"],
|
||||
],
|
||||
[".github/workflows/ios-periphery.yml", ["test/scripts/ios-periphery-comment-workflow.test.ts"]],
|
||||
[
|
||||
".github/workflows/ios-periphery.yml",
|
||||
[
|
||||
"test/scripts/ios-periphery-comment-workflow.test.ts",
|
||||
"test/scripts/periphery-scope-workflows.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
".github/workflows/macos-periphery.yml",
|
||||
["test/scripts/ios-periphery-comment-workflow.test.ts"],
|
||||
[
|
||||
"test/scripts/ios-periphery-comment-workflow.test.ts",
|
||||
"test/scripts/periphery-scope-workflows.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
".github/workflows/shared-openclawkit-periphery.yml",
|
||||
["test/scripts/periphery-intersection.test.ts"],
|
||||
[
|
||||
"test/scripts/periphery-intersection.test.ts",
|
||||
"test/scripts/periphery-scope-workflows.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
".github/workflows/live-media-runner-image.yml",
|
||||
|
||||
@@ -88,74 +88,6 @@ function commenterScript(): string {
|
||||
return script;
|
||||
}
|
||||
|
||||
function scopeScript(): string {
|
||||
const workflow = parse(readFileSync(PRODUCER_WORKFLOW_PATH, "utf8")) as ProducerWorkflow;
|
||||
const step = workflow.jobs?.scope?.steps?.find((candidate) => candidate.id === "scope");
|
||||
const script = step?.with?.script;
|
||||
if (!script) {
|
||||
throw new Error("missing iOS Periphery scope script");
|
||||
}
|
||||
return script;
|
||||
}
|
||||
|
||||
async function runScope(options: {
|
||||
diffExitCode?: number;
|
||||
draft?: boolean;
|
||||
eventName?: string;
|
||||
files?: Array<string | { filename: string; previous_filename?: string }>;
|
||||
}): Promise<string | undefined> {
|
||||
const outputs = new Map<string, string>();
|
||||
const core = {
|
||||
setOutput(name: string, value: string) {
|
||||
outputs.set(name, value);
|
||||
},
|
||||
};
|
||||
const context = {
|
||||
eventName: options.eventName ?? "pull_request",
|
||||
payload: {
|
||||
pull_request: {
|
||||
base: { sha: "base-sha" },
|
||||
draft: options.draft ?? false,
|
||||
number: 123,
|
||||
},
|
||||
},
|
||||
repo: {
|
||||
owner: "openclaw",
|
||||
repo: "openclaw",
|
||||
},
|
||||
};
|
||||
const exec = {
|
||||
async getExecOutput(command: string, args: string[]) {
|
||||
if (command !== "git" || args.slice(0, 5).join(" ") !== "diff --quiet base-sha HEAD --") {
|
||||
throw new Error(`unexpected scope command: ${command} ${args.join(" ")}`);
|
||||
}
|
||||
if (options.diffExitCode !== undefined) {
|
||||
return { exitCode: options.diffExitCode };
|
||||
}
|
||||
const pathspecs = args.slice(5);
|
||||
const filenames = (options.files ?? []).flatMap((file) =>
|
||||
typeof file === "string"
|
||||
? [file]
|
||||
: [file.filename, file.previous_filename].filter((name): name is string => Boolean(name)),
|
||||
);
|
||||
const changed = filenames.some((filename) =>
|
||||
pathspecs.some((pathspec) =>
|
||||
pathspec.endsWith("/") ? filename.startsWith(pathspec) : filename === pathspec,
|
||||
),
|
||||
);
|
||||
return { exitCode: changed ? 1 : 0 };
|
||||
},
|
||||
};
|
||||
const execute = compileFunction(`return (async () => {\n${scopeScript()}\n})();`, [
|
||||
"context",
|
||||
"core",
|
||||
"exec",
|
||||
]) as (context: unknown, core: unknown, exec: unknown) => Promise<void>;
|
||||
|
||||
await execute(context, core, exec);
|
||||
return outputs.get("should-scan");
|
||||
}
|
||||
|
||||
async function runCommenter(
|
||||
artifact: Artifact,
|
||||
archiveData: Buffer,
|
||||
@@ -476,69 +408,6 @@ describe("iOS Periphery comment workflow", () => {
|
||||
expect(workflow.jobs?.scan?.["runs-on"]).toContain("github.run_attempt > 1");
|
||||
}
|
||||
});
|
||||
|
||||
it("runs scope detection for PR transitions that can clear stale findings", () => {
|
||||
const workflow = parse(readFileSync(PRODUCER_WORKFLOW_PATH, "utf8")) as ProducerWorkflow;
|
||||
|
||||
expect(workflow.on?.pull_request?.types).toContain("converted_to_draft");
|
||||
expect(workflow.on?.pull_request?.paths).toBeUndefined();
|
||||
expect(() =>
|
||||
compileFunction(`return (async () => {\n${scopeScript()}\n})();`, [
|
||||
"context",
|
||||
"core",
|
||||
"exec",
|
||||
]),
|
||||
).not.toThrow();
|
||||
expect(scopeScript()).toContain("exec.getExecOutput");
|
||||
expect(scopeScript()).not.toContain("pulls.listFiles");
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
expected: "false",
|
||||
files: ["apps/ios/Sources/Test.swift"],
|
||||
name: "draft pull request",
|
||||
options: { draft: true },
|
||||
},
|
||||
{
|
||||
expected: "true",
|
||||
files: ["apps/ios/Sources/Test.swift"],
|
||||
name: "iOS change",
|
||||
options: {},
|
||||
},
|
||||
{
|
||||
expected: "false",
|
||||
files: ["docs/index.md"],
|
||||
name: "out-of-scope change",
|
||||
options: {},
|
||||
},
|
||||
{
|
||||
expected: "true",
|
||||
files: [
|
||||
{
|
||||
filename: "docs/Moved.swift",
|
||||
previous_filename: "apps/ios/Sources/Moved.swift",
|
||||
},
|
||||
],
|
||||
name: "iOS file renamed out of scope",
|
||||
options: {},
|
||||
},
|
||||
{
|
||||
expected: "true",
|
||||
files: [],
|
||||
name: "manual dispatch",
|
||||
options: { eventName: "workflow_dispatch" },
|
||||
},
|
||||
])("sets scope output for $name", async ({ expected, files, options }) => {
|
||||
await expect(runScope({ ...options, files })).resolves.toBe(expected);
|
||||
});
|
||||
|
||||
it("fails closed when git cannot compare the base and head", async () => {
|
||||
await expect(runScope({ diffExitCode: 128 })).rejects.toThrow(
|
||||
"git diff failed with exit code 128",
|
||||
);
|
||||
});
|
||||
|
||||
it("accepts a valid small Periphery artifact", async () => {
|
||||
const archive = makeZip({
|
||||
"periphery.json": "[]\n",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { compileFunction } from "node:vm";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parse } from "yaml";
|
||||
import {
|
||||
@@ -167,43 +166,4 @@ describe("shared OpenClawKit Periphery workflow", () => {
|
||||
);
|
||||
expect(macosScan?.run).not.toContain("--exclude-tests");
|
||||
});
|
||||
|
||||
it("scopes native consumer, shared package, and workflow changes", async () => {
|
||||
const script = workflow.jobs?.scope?.steps?.find((step) => step.id === "scope")?.with?.script;
|
||||
expect(script).toBeTruthy();
|
||||
const execute = compileFunction(`return (async () => {\n${script}\n})();`, [
|
||||
"context",
|
||||
"core",
|
||||
"exec",
|
||||
]) as (context: unknown, core: unknown, exec: unknown) => Promise<void>;
|
||||
|
||||
for (const filename of [
|
||||
"apps/ios/Sources/App.swift",
|
||||
"apps/macos/Sources/OpenClaw/App.swift",
|
||||
"apps/shared/OpenClawKit/Sources/OpenClawKit/Example.swift",
|
||||
WORKFLOW_PATH,
|
||||
]) {
|
||||
const outputs = new Map<string, string>();
|
||||
await execute(
|
||||
{
|
||||
eventName: "pull_request",
|
||||
payload: { pull_request: { base: { sha: "base-sha" }, draft: false, number: 1 } },
|
||||
repo: {},
|
||||
},
|
||||
{ setOutput: (name: string, value: string) => outputs.set(name, value) },
|
||||
{
|
||||
async getExecOutput(command: string, args: string[]) {
|
||||
expect(command).toBe("git");
|
||||
expect(args.slice(0, 5)).toEqual(["diff", "--quiet", "base-sha", "HEAD", "--"]);
|
||||
const pathspecs = args.slice(5);
|
||||
const changed = pathspecs.some((pathspec) =>
|
||||
pathspec.endsWith("/") ? filename.startsWith(pathspec) : filename === pathspec,
|
||||
);
|
||||
return { exitCode: changed ? 1 : 0 };
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(outputs.get("should-scan"), filename).toBe("true");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { compileFunction } from "node:vm";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { parse } from "yaml";
|
||||
import { cleanupTempDirs, makeTempRepoRoot } from "../helpers/temp-repo.js";
|
||||
|
||||
const WORKFLOW_CASES = [
|
||||
{
|
||||
name: "iOS",
|
||||
path: ".github/workflows/ios-periphery.yml",
|
||||
scopedPath: "apps/ios/Sources/Test.swift",
|
||||
},
|
||||
{
|
||||
name: "macOS",
|
||||
path: ".github/workflows/macos-periphery.yml",
|
||||
scopedPath: "apps/macos/Sources/OpenClaw/Test.swift",
|
||||
},
|
||||
{
|
||||
name: "shared OpenClawKit",
|
||||
path: ".github/workflows/shared-openclawkit-periphery.yml",
|
||||
scopedPath: "apps/shared/OpenClawKit/Sources/OpenClawKit/Test.swift",
|
||||
},
|
||||
] as const;
|
||||
|
||||
type WorkflowStep = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
with?: {
|
||||
"fetch-depth"?: number;
|
||||
script?: string;
|
||||
};
|
||||
};
|
||||
|
||||
type ScopeWorkflow = {
|
||||
on?: {
|
||||
pull_request?: {
|
||||
paths?: string[];
|
||||
types?: string[];
|
||||
};
|
||||
};
|
||||
jobs?: {
|
||||
scope?: {
|
||||
steps?: WorkflowStep[];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
type ScopeOptions = {
|
||||
diffExitCode?: number;
|
||||
draft?: boolean;
|
||||
eventName?: string;
|
||||
files?: Array<string | { filename: string; previous_filename?: string }>;
|
||||
};
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
afterEach(() => {
|
||||
cleanupTempDirs(tempDirs);
|
||||
});
|
||||
|
||||
function readWorkflow(workflowPath: string): ScopeWorkflow {
|
||||
return parse(readFileSync(workflowPath, "utf8")) as ScopeWorkflow;
|
||||
}
|
||||
|
||||
function scopeScript(workflowPath: string): string {
|
||||
const step = readWorkflow(workflowPath).jobs?.scope?.steps?.find(
|
||||
(candidate) => candidate.id === "scope",
|
||||
);
|
||||
if (!step?.with?.script) {
|
||||
throw new Error(`missing Periphery scope script in ${workflowPath}`);
|
||||
}
|
||||
return step.with.script;
|
||||
}
|
||||
|
||||
async function runScope(workflowPath: string, options: ScopeOptions): Promise<string | undefined> {
|
||||
const outputs = new Map<string, string>();
|
||||
const context = {
|
||||
eventName: options.eventName ?? "pull_request",
|
||||
payload: { pull_request: { draft: options.draft ?? false, number: 123 } },
|
||||
};
|
||||
const exec = {
|
||||
async getExecOutput(command: string, args: string[]) {
|
||||
if (command !== "git" || args.slice(0, 5).join(" ") !== "diff --quiet HEAD^1 HEAD --") {
|
||||
throw new Error(`unexpected scope command: ${command} ${args.join(" ")}`);
|
||||
}
|
||||
if (options.diffExitCode !== undefined) {
|
||||
return { exitCode: options.diffExitCode };
|
||||
}
|
||||
const pathspecs = args.slice(5);
|
||||
const filenames = (options.files ?? []).flatMap((file) =>
|
||||
typeof file === "string"
|
||||
? [file]
|
||||
: [file.filename, file.previous_filename].filter((name): name is string => Boolean(name)),
|
||||
);
|
||||
const changed = filenames.some((filename) =>
|
||||
pathspecs.some((pathspec) =>
|
||||
pathspec.endsWith("/") ? filename.startsWith(pathspec) : filename === pathspec,
|
||||
),
|
||||
);
|
||||
return { exitCode: changed ? 1 : 0 };
|
||||
},
|
||||
};
|
||||
const execute = compileFunction(`return (async () => {\n${scopeScript(workflowPath)}\n})();`, [
|
||||
"context",
|
||||
"core",
|
||||
"exec",
|
||||
]) as (context: unknown, core: unknown, exec: unknown) => Promise<void>;
|
||||
|
||||
await execute(
|
||||
context,
|
||||
{ setOutput: (name: string, value: string) => outputs.set(name, value) },
|
||||
exec,
|
||||
);
|
||||
return outputs.get("should-scan");
|
||||
}
|
||||
|
||||
function git(repoRoot: string, args: string[]): string {
|
||||
const result = spawnSync("git", args, { cwd: repoRoot, encoding: "utf8" });
|
||||
if (result.status !== 0) {
|
||||
throw new Error(`git ${args.join(" ")} failed: ${result.stderr}`);
|
||||
}
|
||||
return result.stdout.trim();
|
||||
}
|
||||
|
||||
function writeFixture(repoRoot: string, relativePath: string, contents: string): void {
|
||||
const filePath = path.join(repoRoot, relativePath);
|
||||
mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
writeFileSync(filePath, contents, "utf8");
|
||||
}
|
||||
|
||||
describe("Periphery scope workflows", () => {
|
||||
it.each(WORKFLOW_CASES)("uses the synthetic merge parent for $name scope", ({ path }) => {
|
||||
const workflow = readWorkflow(path);
|
||||
const steps = workflow.jobs?.scope?.steps ?? [];
|
||||
const checkout = steps.find((step) => step.name === "Checkout");
|
||||
const script = scopeScript(path);
|
||||
|
||||
expect(workflow.on?.pull_request?.types).toContain("converted_to_draft");
|
||||
expect(workflow.on?.pull_request?.paths).toBeUndefined();
|
||||
expect(checkout?.with?.["fetch-depth"]).toBe(2);
|
||||
expect(steps.some((step) => step.name === "Ensure base commit")).toBe(false);
|
||||
expect(script).toContain('"HEAD^1"');
|
||||
expect(script).not.toContain("pulls.listFiles");
|
||||
expect(() =>
|
||||
compileFunction(`return (async () => {\n${script}\n})();`, ["context", "core", "exec"]),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it.each(WORKFLOW_CASES)("selects only $name scope changes", async ({ path, scopedPath }) => {
|
||||
await expect(runScope(path, { files: [scopedPath] })).resolves.toBe("true");
|
||||
await expect(runScope(path, { files: ["docs/index.md"] })).resolves.toBe("false");
|
||||
await expect(runScope(path, { draft: true, files: [scopedPath] })).resolves.toBe("false");
|
||||
await expect(runScope(path, { eventName: "workflow_dispatch" })).resolves.toBe("true");
|
||||
await expect(
|
||||
runScope(path, {
|
||||
files: [{ filename: "docs/Moved.swift", previous_filename: scopedPath }],
|
||||
}),
|
||||
).resolves.toBe("true");
|
||||
await expect(runScope(path, { diffExitCode: 128 })).rejects.toThrow(
|
||||
"git diff failed with exit code 128",
|
||||
);
|
||||
});
|
||||
|
||||
it("ignores scoped files added only by base-branch drift", async () => {
|
||||
const repoRoot = makeTempRepoRoot(tempDirs, "openclaw-periphery-scope-");
|
||||
git(repoRoot, ["init", "--initial-branch=main"]);
|
||||
git(repoRoot, ["config", "user.name", "OpenClaw Test"]);
|
||||
git(repoRoot, ["config", "user.email", "openclaw-test@example.com"]);
|
||||
|
||||
writeFixture(repoRoot, "docs/base.md", "base\n");
|
||||
git(repoRoot, ["add", "."]);
|
||||
git(repoRoot, ["commit", "-m", "base"]);
|
||||
const eventBase = git(repoRoot, ["rev-parse", "HEAD"]);
|
||||
|
||||
git(repoRoot, ["switch", "-c", "pr"]);
|
||||
writeFixture(repoRoot, "docs/pr.md", "pull request\n");
|
||||
git(repoRoot, ["add", "."]);
|
||||
git(repoRoot, ["commit", "-m", "pull request"]);
|
||||
|
||||
git(repoRoot, ["switch", "main"]);
|
||||
writeFixture(
|
||||
repoRoot,
|
||||
"apps/shared/OpenClawKit/Sources/OpenClawKit/Main.swift",
|
||||
"struct Main {}\n",
|
||||
);
|
||||
git(repoRoot, ["add", "."]);
|
||||
git(repoRoot, ["commit", "-m", "advance main"]);
|
||||
git(repoRoot, ["merge", "--no-ff", "pr", "-m", "synthetic merge"]);
|
||||
|
||||
const oldDiff = spawnSync(
|
||||
"git",
|
||||
["diff", "--quiet", eventBase, "HEAD", "--", "apps/shared/OpenClawKit/"],
|
||||
{ cwd: repoRoot },
|
||||
);
|
||||
expect(oldDiff.status).toBe(1);
|
||||
|
||||
const outputs = new Map<string, string>();
|
||||
const execute = compileFunction(
|
||||
`return (async () => {\n${scopeScript(".github/workflows/shared-openclawkit-periphery.yml")}\n})();`,
|
||||
["context", "core", "exec"],
|
||||
) as (context: unknown, core: unknown, exec: unknown) => Promise<void>;
|
||||
await execute(
|
||||
{ eventName: "pull_request", payload: { pull_request: { draft: false, number: 123 } } },
|
||||
{ setOutput: (name: string, value: string) => outputs.set(name, value) },
|
||||
{
|
||||
async getExecOutput(command: string, args: string[]) {
|
||||
const result = spawnSync(command, args, { cwd: repoRoot });
|
||||
return { exitCode: result.status ?? 128 };
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(outputs.get("should-scan")).toBe("false");
|
||||
expect(git(repoRoot, ["diff", "--name-only", "HEAD^1", "HEAD"])).toBe("docs/pr.md");
|
||||
});
|
||||
});
|
||||
@@ -1376,6 +1376,42 @@ describe("scripts/test-projects changed-target routing", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("routes Periphery workflow edits through their scope regression tests", () => {
|
||||
const workflowTargets = new Map([
|
||||
[
|
||||
".github/workflows/ios-periphery.yml",
|
||||
[
|
||||
"test/scripts/ios-periphery-comment-workflow.test.ts",
|
||||
"test/scripts/periphery-scope-workflows.test.ts",
|
||||
"test/scripts/ci-workflow-guards.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
".github/workflows/macos-periphery.yml",
|
||||
[
|
||||
"test/scripts/ios-periphery-comment-workflow.test.ts",
|
||||
"test/scripts/periphery-scope-workflows.test.ts",
|
||||
"test/scripts/ci-workflow-guards.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
".github/workflows/shared-openclawkit-periphery.yml",
|
||||
[
|
||||
"test/scripts/periphery-intersection.test.ts",
|
||||
"test/scripts/periphery-scope-workflows.test.ts",
|
||||
"test/scripts/ci-workflow-guards.test.ts",
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
for (const [workflowPath, targets] of workflowTargets) {
|
||||
expect(resolveChangedTestTargetPlan([workflowPath]), workflowPath).toEqual({
|
||||
mode: "targets",
|
||||
targets,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps Mantis proof workflow edits on workflow evidence regression tests", () => {
|
||||
const packageAcceptanceTargets = [
|
||||
"test/scripts/package-acceptance-workflow.test.ts",
|
||||
|
||||
Reference in New Issue
Block a user