mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
ci: gate PR edit automation by changed field (#95010)
* ci: gate PR edit automation by changed field * ci: include title edits in ClawSweeper dispatch gate * ci: refine PR edit automation gates Co-authored-by: Sash Zats <sash@zats.io> * test(ci): require boolean workflow matcher Co-authored-by: Sash Zats <sash@zats.io> --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -27,6 +27,15 @@ permissions: {}
|
||||
|
||||
jobs:
|
||||
label:
|
||||
if: >-
|
||||
${{
|
||||
github.event_name == 'pull_request_target' &&
|
||||
(
|
||||
github.event.action != 'edited' ||
|
||||
github.event.changes.title ||
|
||||
github.event.changes.base
|
||||
)
|
||||
}}
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
@@ -45,11 +54,13 @@ jobs:
|
||||
app-id: "2971289"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
|
||||
- uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6
|
||||
if: ${{ github.event.action != 'edited' || github.event.changes.base }}
|
||||
with:
|
||||
configuration-path: .github/labeler.yml
|
||||
repo-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
sync-labels: true
|
||||
- name: Apply PR size label
|
||||
if: ${{ github.event.action != 'edited' || github.event.changes.base }}
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
@@ -139,6 +150,7 @@ jobs:
|
||||
labels: [targetSizeLabel],
|
||||
});
|
||||
- name: Apply maintainer or trusted-contributor label
|
||||
if: ${{ github.event.action != 'edited' }}
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
@@ -210,6 +222,7 @@ jobs:
|
||||
// });
|
||||
// }
|
||||
- name: Apply beta-blocker title label
|
||||
if: ${{ github.event.action != 'edited' || github.event.changes.title }}
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
@@ -263,6 +276,7 @@ jobs:
|
||||
});
|
||||
}
|
||||
- name: Apply too-many-prs label
|
||||
if: ${{ github.event.action != 'edited' }}
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
|
||||
@@ -16,6 +16,12 @@ permissions: {}
|
||||
jobs:
|
||||
real-behavior-proof:
|
||||
name: Real behavior proof
|
||||
if: >-
|
||||
${{
|
||||
github.event.action != 'edited' ||
|
||||
github.event.changes.body ||
|
||||
github.event.changes.base
|
||||
}}
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
|
||||
@@ -701,7 +701,11 @@ const TOOLING_SOURCE_TEST_TARGETS = new Map([
|
||||
["test/scripts/setup-pnpm-store-cache-ensure-node.test.ts"],
|
||||
],
|
||||
[".github/images/live-media-runner/Dockerfile", LIVE_MEDIA_RUNNER_IMAGE_TEST_TARGETS],
|
||||
[".github/workflows/auto-response.yml", ["test/scripts/ci-workflow-guards.test.ts"]],
|
||||
[".github/workflows/ci.yml", ["test/scripts/ci-workflow-guards.test.ts"]],
|
||||
[".github/workflows/clawsweeper-dispatch.yml", ["test/scripts/ci-workflow-guards.test.ts"]],
|
||||
[".github/workflows/labeler.yml", ["test/scripts/ci-workflow-guards.test.ts"]],
|
||||
[".github/workflows/real-behavior-proof.yml", ["test/scripts/ci-workflow-guards.test.ts"]],
|
||||
[
|
||||
".github/workflows/security-sensitive-guard.yml",
|
||||
["test/scripts/security-sensitive-guard-workflow.test.ts"],
|
||||
|
||||
@@ -376,6 +376,19 @@ function readCriticalQualityWorkflow() {
|
||||
return readFileSync(".github/workflows/codeql-critical-quality.yml", "utf8");
|
||||
}
|
||||
|
||||
function readWorkflow(path: string) {
|
||||
return parse(readFileSync(path, "utf8"));
|
||||
}
|
||||
|
||||
const PULL_REQUEST_EDIT_FIELDS = ["title", "body", "base"] as const;
|
||||
|
||||
function readPullRequestEditFields(condition: unknown) {
|
||||
const expression = typeof condition === "string" ? condition : "";
|
||||
return PULL_REQUEST_EDIT_FIELDS.filter((field) =>
|
||||
expression.includes(`github.event.changes.${field}`),
|
||||
);
|
||||
}
|
||||
|
||||
function readTrackedText(relativePath: string): string {
|
||||
if (existsSync(relativePath)) {
|
||||
return readFileSync(relativePath, "utf8");
|
||||
@@ -665,6 +678,52 @@ function runGeneratedPublisherScenario(
|
||||
}
|
||||
|
||||
describe("ci workflow guards", () => {
|
||||
it("routes PR edited metadata only to interested automation", () => {
|
||||
const autoResponse = readWorkflow(".github/workflows/auto-response.yml");
|
||||
const clawsweeperDispatch = readWorkflow(".github/workflows/clawsweeper-dispatch.yml");
|
||||
const labeler = readWorkflow(".github/workflows/labeler.yml");
|
||||
const realBehaviorProof = readWorkflow(".github/workflows/real-behavior-proof.yml");
|
||||
|
||||
for (const workflow of [autoResponse, clawsweeperDispatch, labeler, realBehaviorProof]) {
|
||||
expect(workflow.on.pull_request_target.types).toContain("edited");
|
||||
}
|
||||
|
||||
expect({
|
||||
autoResponse: readPullRequestEditFields(autoResponse.jobs["auto-response"].if),
|
||||
clawsweeperDispatch: readPullRequestEditFields(clawsweeperDispatch.jobs.dispatch.if),
|
||||
labeler: readPullRequestEditFields(labeler.jobs.label.if),
|
||||
realBehaviorProof: readPullRequestEditFields(
|
||||
realBehaviorProof.jobs["real-behavior-proof"].if,
|
||||
),
|
||||
}).toEqual({
|
||||
autoResponse: [],
|
||||
clawsweeperDispatch: [],
|
||||
labeler: ["title", "base"],
|
||||
realBehaviorProof: ["body", "base"],
|
||||
});
|
||||
|
||||
const labelerSteps = labeler.jobs.label.steps;
|
||||
const changedFieldsForStep = (matcher: (step: WorkflowStep) => boolean) =>
|
||||
readPullRequestEditFields(labelerSteps.find(matcher)?.if);
|
||||
expect({
|
||||
pathLabels: changedFieldsForStep(
|
||||
(step) => step.uses?.startsWith("actions/labeler@") === true,
|
||||
),
|
||||
size: changedFieldsForStep((step) => step.name === "Apply PR size label"),
|
||||
contributor: changedFieldsForStep(
|
||||
(step) => step.name === "Apply maintainer or trusted-contributor label",
|
||||
),
|
||||
betaBlocker: changedFieldsForStep((step) => step.name === "Apply beta-blocker title label"),
|
||||
activePrLimit: changedFieldsForStep((step) => step.name === "Apply too-many-prs label"),
|
||||
}).toEqual({
|
||||
pathLabels: ["base"],
|
||||
size: ["base"],
|
||||
contributor: [],
|
||||
betaBlocker: ["title"],
|
||||
activePrLimit: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("makes the hosted release-gate fallback explicit and exact-SHA only", () => {
|
||||
const workflow = readCiWorkflow();
|
||||
const releaseGate = workflow.on.workflow_dispatch.inputs.release_gate;
|
||||
|
||||
@@ -1292,6 +1292,20 @@ describe("scripts/test-projects changed-target routing", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps PR automation workflow edits on workflow guard tests", () => {
|
||||
for (const workflowPath of [
|
||||
".github/workflows/auto-response.yml",
|
||||
".github/workflows/clawsweeper-dispatch.yml",
|
||||
".github/workflows/labeler.yml",
|
||||
".github/workflows/real-behavior-proof.yml",
|
||||
]) {
|
||||
expect(resolveChangedTestTargetPlan([workflowPath])).toEqual({
|
||||
mode: "targets",
|
||||
targets: ["test/scripts/ci-workflow-guards.test.ts"],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps security-sensitive guard workflow edits on guard workflow tests", () => {
|
||||
expect(
|
||||
resolveChangedTestTargetPlan([".github/workflows/security-sensitive-guard.yml"]),
|
||||
|
||||
Reference in New Issue
Block a user