From 131dc4eaeb63b4ad71394f62b25d751faa3fa29b Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 30 May 2026 10:57:03 +0200 Subject: [PATCH] fix(test): route workflow helper changes --- CHANGELOG.md | 1 + scripts/test-projects.test-support.mjs | 13 +++ ...mposite-action-input-interpolation.test.ts | 83 +++++++++++++++++++ test/scripts/test-projects.test.ts | 25 ++++++ 4 files changed, 122 insertions(+) create mode 100644 test/scripts/check-composite-action-input-interpolation.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 70d5f759ee5..0f4f586d42b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ Docs: https://docs.openclaw.ai - Release/CI/E2E: bound release candidate reads, beta smoke REST calls, changelog restore, kitchen-sink and bundled plugin readiness probes, secret-provider probes, Vitest routing, and mainline test flakes. (#88127, #88137, #88155, #88160) - CI/Crabbox: keep default runner capacity spot-only and provider-neutral so OpenClaw remote validation does not silently fall back to on-demand leases or stale AWS region hints. - CI/Crabbox: route Crabbox wrapper and Testbox workflow edits to their regression tests so changed-test gates do not silently run zero specs. +- CI/workflows: route workflow sanity helper edits to their guard tests and cover composite-action input interpolation checks. - Performance: reuse prepared provider handles, strict tool schemas, gateway runtime metadata, session maintenance config, plugin metadata, bundled skill allowlists, package-local plugin artifacts, and single-entry store writes. ## 2026.5.28 diff --git a/scripts/test-projects.test-support.mjs b/scripts/test-projects.test-support.mjs index 10dd58ffdf6..737b91a86f2 100644 --- a/scripts/test-projects.test-support.mjs +++ b/scripts/test-projects.test-support.mjs @@ -380,8 +380,21 @@ const TOOLING_SOURCE_TEST_TARGETS = new Map([ ["scripts/changed-lanes.mjs", ["test/scripts/changed-lanes.test.ts"]], ["scripts/check.mjs", ["test/scripts/check.test.ts"]], ["scripts/check-changed.mjs", ["test/scripts/changed-lanes.test.ts"]], + [ + "scripts/check-composite-action-input-interpolation.py", + ["test/scripts/check-composite-action-input-interpolation.test.ts"], + ], ["scripts/check-deadcode-unused-files.mjs", ["test/scripts/check-deadcode-unused-files.test.ts"]], ["scripts/check-dynamic-import-warts.mjs", ["test/scripts/check-dynamic-import-warts.test.ts"]], + ["scripts/check-no-conflict-markers.mjs", ["test/scripts/check-no-conflict-markers.test.ts"]], + [ + "scripts/check-workflows.mjs", + [ + "test/scripts/check-composite-action-input-interpolation.test.ts", + "test/scripts/check-no-conflict-markers.test.ts", + "test/scripts/ci-workflow-guards.test.ts", + ], + ], ["scripts/ci-docker-pull-retry.sh", ["test/scripts/ci-docker-pull-retry.test.ts"]], ["scripts/control-ui-i18n.ts", ["test/scripts/control-ui-i18n.test.ts"]], [ diff --git a/test/scripts/check-composite-action-input-interpolation.test.ts b/test/scripts/check-composite-action-input-interpolation.test.ts new file mode 100644 index 00000000000..5c5b189912d --- /dev/null +++ b/test/scripts/check-composite-action-input-interpolation.test.ts @@ -0,0 +1,83 @@ +import { spawnSync } from "node:child_process"; +import fs from "node:fs"; +import path from "node:path"; +import { describe, expect, it } from "vitest"; +import { createScriptTestHarness } from "./test-helpers.js"; + +const { createTempDir } = createScriptTestHarness(); +const scriptPath = path.resolve("scripts/check-composite-action-input-interpolation.py"); + +function writeAction(rootDir: string, name: string, source: string): void { + const actionPath = path.join(rootDir, ".github", "actions", name, "action.yml"); + fs.mkdirSync(path.dirname(actionPath), { recursive: true }); + fs.writeFileSync(actionPath, source, "utf8"); +} + +function runCheck(cwd: string) { + return spawnSync("python3", [scriptPath], { + cwd, + encoding: "utf8", + }); +} + +describe("check-composite-action-input-interpolation", () => { + it("rejects direct inputs interpolation inside composite run blocks", () => { + const rootDir = createTempDir("openclaw-composite-action-inputs-"); + writeAction( + rootDir, + "unsafe", + [ + "name: unsafe", + "runs:", + " using: composite", + " steps:", + " - shell: bash", + " run: |", + ' echo "${{ inputs.token }}"', + ].join("\n"), + ); + + const result = runCheck(rootDir); + + expect(result.status).toBe(1); + expect(result.stdout).toContain("Disallowed direct inputs interpolation"); + expect(result.stdout).toContain(".github/actions/unsafe/action.yml:7"); + expect(result.stdout).toContain("Use env: and reference shell variables instead."); + }); + + it("allows env indirection and ignores non-composite actions", () => { + const rootDir = createTempDir("openclaw-composite-action-inputs-"); + writeAction( + rootDir, + "safe", + [ + "name: safe", + "runs:", + " using: composite", + " steps:", + " - shell: bash", + " env:", + " TOKEN: ${{ inputs.token }}", + " run: |", + ' echo "$TOKEN"', + ].join("\n"), + ); + writeAction( + rootDir, + "non-composite", + [ + "name: non-composite", + "runs:", + " using: node24", + " main: dist/index.js", + " steps:", + ' - run: echo "${{ inputs.token }}"', + ].join("\n"), + ); + + const result = runCheck(rootDir); + + expect(result.status).toBe(0); + expect(result.stdout).toContain("No direct inputs interpolation found"); + }); +}); diff --git a/test/scripts/test-projects.test.ts b/test/scripts/test-projects.test.ts index 93091360b63..1ecefc16b86 100644 --- a/test/scripts/test-projects.test.ts +++ b/test/scripts/test-projects.test.ts @@ -385,6 +385,31 @@ describe("scripts/test-projects changed-target routing", () => { } }); + it("keeps workflow sanity script edits on workflow guard tests", () => { + expect(resolveChangedTestTargetPlan(["scripts/check-workflows.mjs"])).toEqual({ + mode: "targets", + targets: [ + "test/scripts/check-composite-action-input-interpolation.test.ts", + "test/scripts/check-no-conflict-markers.test.ts", + "test/scripts/ci-workflow-guards.test.ts", + ], + }); + }); + + it("keeps workflow helper guard edits on their regression tests", () => { + expect( + resolveChangedTestTargetPlan(["scripts/check-composite-action-input-interpolation.py"]), + ).toEqual({ + mode: "targets", + targets: ["test/scripts/check-composite-action-input-interpolation.test.ts"], + }); + + expect(resolveChangedTestTargetPlan(["scripts/check-no-conflict-markers.mjs"])).toEqual({ + mode: "targets", + targets: ["test/scripts/check-no-conflict-markers.test.ts"], + }); + }); + it("routes explicit tooling implementation files to owner tests", () => { expect( findUnmatchedExplicitTestTargets([