From da9700903c72afc46f07652a82fd9ef769687f77 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 23 Apr 2026 00:15:40 +0100 Subject: [PATCH] ci: skip no-op changed-scope fanout --- scripts/ci-changed-scope.mjs | 40 ++++++++++++----------- src/scripts/ci-changed-scope.test.ts | 48 ++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 18 deletions(-) diff --git a/scripts/ci-changed-scope.mjs b/scripts/ci-changed-scope.mjs index aa03ec836ad..378858b596b 100644 --- a/scripts/ci-changed-scope.mjs +++ b/scripts/ci-changed-scope.mjs @@ -3,6 +3,26 @@ import { appendFileSync } from "node:fs"; /** @typedef {{ runNode: boolean; runMacos: boolean; runAndroid: boolean; runWindows: boolean; runSkillsPython: boolean; runChangedSmoke: boolean; runControlUiI18n: boolean }} ChangedScope */ +const FULL_SCOPE = { + runNode: true, + runMacos: true, + runAndroid: true, + runWindows: true, + runSkillsPython: true, + runChangedSmoke: true, + runControlUiI18n: true, +}; + +const EMPTY_SCOPE = { + runNode: false, + runMacos: false, + runAndroid: false, + runWindows: false, + runSkillsPython: false, + runChangedSmoke: false, + runControlUiI18n: false, +}; + const DOCS_PATH_RE = /^(docs\/|.*\.mdx?$)/; const SKILLS_PYTHON_SCOPE_RE = /^(skills\/|pyproject\.toml$)/; const INSTALL_SMOKE_WORKFLOW_SCOPE_RE = /^\.github\/workflows\/install-smoke\.yml$/; @@ -183,27 +203,11 @@ if (isDirectRun()) { try { const changedPaths = listChangedPaths(args.base, args.head); if (changedPaths.length === 0) { - writeGitHubOutput({ - runNode: true, - runMacos: true, - runAndroid: true, - runWindows: true, - runSkillsPython: true, - runChangedSmoke: true, - runControlUiI18n: true, - }); + writeGitHubOutput(EMPTY_SCOPE); process.exit(0); } writeGitHubOutput(detectChangedScope(changedPaths)); } catch { - writeGitHubOutput({ - runNode: true, - runMacos: true, - runAndroid: true, - runWindows: true, - runSkillsPython: true, - runChangedSmoke: true, - runControlUiI18n: true, - }); + writeGitHubOutput(FULL_SCOPE); } } diff --git a/src/scripts/ci-changed-scope.test.ts b/src/scripts/ci-changed-scope.test.ts index 9c9c35cffea..64804e07a37 100644 --- a/src/scripts/ci-changed-scope.test.ts +++ b/src/scripts/ci-changed-scope.test.ts @@ -1,3 +1,4 @@ +import { execFileSync } from "node:child_process"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; @@ -19,6 +20,7 @@ const { detectChangedScope, listChangedPaths } = }; const markerPaths: string[] = []; +const tempDirs: string[] = []; afterEach(() => { for (const markerPath of markerPaths) { @@ -27,8 +29,25 @@ afterEach(() => { } catch {} } markerPaths.length = 0; + for (const tempDir of tempDirs) { + fs.rmSync(tempDir, { force: true, recursive: true }); + } + tempDirs.length = 0; }); +function parseGitHubOutput(output: string): Record { + return Object.fromEntries( + output + .trim() + .split("\n") + .filter(Boolean) + .map((line) => { + const separator = line.indexOf("="); + return [line.slice(0, separator), line.slice(separator + 1)]; + }), + ); +} + describe("detectChangedScope", () => { it("fails safe when no paths are provided", () => { expect(detectChangedScope([])).toEqual({ @@ -333,4 +352,33 @@ describe("detectChangedScope", () => { expect(() => listChangedPaths(injectedBase, "HEAD")).toThrow(); expect(fs.existsSync(markerPath)).toBe(false); }); + + it("keeps direct CLI preflight empty diffs as no-op scope", () => { + const repoDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-ci-scope-empty-")); + tempDirs.push(repoDir); + const outputPath = path.join(repoDir, "github-output.txt"); + const scriptPath = path.resolve("scripts/ci-changed-scope.mjs"); + + execFileSync("git", ["init", "-b", "main"], { cwd: repoDir }); + execFileSync("git", ["config", "user.email", "ci@example.invalid"], { cwd: repoDir }); + execFileSync("git", ["config", "user.name", "CI"], { cwd: repoDir }); + fs.writeFileSync(path.join(repoDir, "README.md"), "test\n", "utf8"); + execFileSync("git", ["add", "README.md"], { cwd: repoDir }); + execFileSync("git", ["commit", "-m", "test"], { cwd: repoDir }); + + execFileSync(process.execPath, [scriptPath, "--base", "HEAD", "--head", "HEAD"], { + cwd: repoDir, + env: { ...process.env, GITHUB_OUTPUT: outputPath }, + }); + + expect(parseGitHubOutput(fs.readFileSync(outputPath, "utf8"))).toEqual({ + run_node: "false", + run_macos: "false", + run_android: "false", + run_windows: "false", + run_skills_python: "false", + run_changed_smoke: "false", + run_control_ui_i18n: "false", + }); + }); });