diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf702ec812f..f302557ba68 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -408,18 +408,19 @@ jobs: - 'scripts/ci/docker-smoke.ts' - 'scripts/generate-compatibility-version.ts' - 'scripts/model-metadata.source.json' - # The image's final RUN imports version.ts to prove the generated compatibility - # manifest; these four files are its whole module closure (Bun.build metafile). - - 'src/routing/compatibility/version.ts' - - 'src/lab/digest.ts' - - 'src/lab/conformance/digest.ts' - - 'src/lab/conformance/jcs.ts' - - 'src/cli/index.ts' + # The image copies and runs src/, then builds the GUI with tsc and Vite. + # Include their inputs without selecting GUI tests or documentation alone. + - 'src/**' - 'package.json' - 'bun.lock' - 'tsconfig.json' - 'gui/package.json' - 'gui/bun.lock' + - 'gui/src/**' + - 'gui/public/**' + - 'gui/index.html' + - 'gui/vite.config.ts' + - 'gui/tsconfig*.json' - '.github/workflows/ci.yml' # scripts/keyring-smoke.ts imports only src/lib/keyring-native.ts; the native binding # itself arrives through package.json and bun.lock. diff --git a/tests/ci-workflows/ci-scope-gaps.test.ts b/tests/ci-workflows/ci-scope-gaps.test.ts index 0729ab6945d..64d74100a1e 100644 --- a/tests/ci-workflows/ci-scope-gaps.test.ts +++ b/tests/ci-workflows/ci-scope-gaps.test.ts @@ -36,6 +36,29 @@ const scriptOf = (job: Job): string => (job.steps ?? []).map(step => step.run ?? const ACTION_PATH = ".github/actions/setup-project-bun/action.yml"; const HELPER_PATH = "native/remote-workspace-helper/src/main.rs"; +describe("Docker build and runtime input scope", () => { + test("selects runtime sources and every GUI build input", () => { + for (const path of [ + "src/config.ts", "src/config/paths.ts", "src/server/index.ts", "src/service.ts", + "gui/src/App.tsx", "gui/src/style.css", "gui/public/icon.svg", "gui/index.html", + "gui/vite.config.ts", "gui/tsconfig.json", "gui/tsconfig.app.json", "gui/tsconfig.node.json", + "gui/package.json", "gui/bun.lock", + ]) { + expect(`${path}:ci=${matches(filters.ci, path)}`).toBe(`${path}:ci=true`); + expect(`${path}:docker=${matches(filters.docker, path)}`).toBe(`${path}:docker=true`); + } + }); + + test("does not select Docker for tests or documentation alone", () => { + for (const path of [ + "tests/server/server-live.test.ts", "gui/tests/claude-code-save.test.ts", + "gui/README.md", "docs-site/src/content/docs/index.mdx", "structure/runtime.md", + ]) { + expect(`${path}:docker=${matches(filters.docker, path)}`).toBe(`${path}:docker=false`); + } + }); +}); + describe("an edit to the setup action alone", () => { test("selects a job that runs the action and checks what it installed", () => { const selected = jobsSelectedBy(filtersMatching(ACTION_PATH)) diff --git a/tests/ci-workflows/ci-scope-reduction.test.ts b/tests/ci-workflows/ci-scope-reduction.test.ts index 8c20565a493..da389e9544f 100644 --- a/tests/ci-workflows/ci-scope-reduction.test.ts +++ b/tests/ci-workflows/ci-scope-reduction.test.ts @@ -54,7 +54,7 @@ const MACOS_JOBS = ["platform-macos", "widget"] as const; /** The two smoke jobs whose matrix legs shrink with the event and native selection. */ const MATRIX_JOBS = ["keyring-smoke", "npm-global-smoke"] as const; -type SelectionInputs = { event_name: string; ci: string; native: string; desktop?: string }; +type SelectionInputs = { event_name: string; ci: string; native: string; desktop?: string; docker?: string }; function term(source: string, inputs: SelectionInputs): string | boolean { const text = source.trim(); @@ -72,6 +72,7 @@ function term(source: string, inputs: SelectionInputs): string | boolean { if (output[1] === "ci") return inputs.ci; if (output[1] === "native") return inputs.native; if (output[1] === "desktop") return inputs.desktop ?? "false"; + if (output[1] === "docker") return inputs.docker ?? "false"; } throw new Error(`unsupported expression term: ${text}`); } @@ -315,6 +316,22 @@ describe("the push trigger", () => { }); }); +describe("Docker PR selection", () => { + test("selects only its Ubuntu smoke for matching inputs without widening event policy", () => { + const job = jobs["docker-smoke"]!; + expect(job["runs-on"]).toBe("ubuntu-latest"); + for (const ci of ["true", "false"]) { + for (const docker of ["true", "false"]) { + expect(evaluate(job.if!, { event_name: "pull_request", ci, docker, native: "false" })) + .toBe(ci === "true" && docker === "true"); + } + } + for (const event_name of ["push", "workflow_dispatch", "schedule"]) { + expect(evaluate(job.if!, { event_name, ci: "false", docker: "false", native: "false" })).toBe(true); + } + }); +}); + describe("the native path filter", () => { test("lists exactly the surfaces the native-gated jobs build", () => { // Exact membership, because every pattern here is a cost decision: widening