From 99d111da015c583f6626cff8f257b1f2a05fc54d Mon Sep 17 00:00:00 2001 From: Naki-ym <81948866+ymnao@users.noreply.github.com> Date: Sun, 19 Jul 2026 03:03:02 +0900 Subject: [PATCH] =?UTF-8?q?test:=20workflow=20paths-mirror=20=E3=81=AE=20d?= =?UTF-8?q?rift=20=E6=A4=9C=E8=A8=BC=E3=83=86=E3=82=B9=E3=83=88=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ci.yml + ci-skip.yml と codeql.yml + codeql-skip.yml の paths-ignore / paths を js-yaml でパースし、pair 間の補完関係と 4 workflow 全体で path list が同一であることを assert - vitest.config.ts に workflows project (node env, test/**/*.test.ts) を追加 - tsconfig.node.json の include に test を追加 PR #357 で codex-review qa-fixture が REPORT-ONLY で指摘した 「paths リスト drift 検証テスト不在」を pair 全体の pattern 昇格として 解消 (HANDOFF §2)。 --- test/workflows/paths-mirror.test.ts | 79 +++++++++++++++++++++++++++++ tsconfig.node.json | 2 +- vitest.config.ts | 9 ++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 test/workflows/paths-mirror.test.ts diff --git a/test/workflows/paths-mirror.test.ts b/test/workflows/paths-mirror.test.ts new file mode 100644 index 0000000..f9b1467 --- /dev/null +++ b/test/workflows/paths-mirror.test.ts @@ -0,0 +1,79 @@ +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { load } from "js-yaml"; +import { describe, expect, it } from "vitest"; + +// ci.yml + ci-skip.yml と codeql.yml + codeql-skip.yml は +// paths-ignore (実 workflow) と paths (stub workflow) が完全補完である必要があり、 +// 片側だけ変わると docs-only PR で required check の状態が壊れる +// (未生成 → BLOCKED、または stub 側が起動せず ci.yml が実 job で走る)。 +// 4 workflow 間で path 定義の drift を防ぐ pattern-level 検証。 + +const WORKFLOWS_DIR = join(process.cwd(), ".github", "workflows"); + +type WorkflowEvents = { + push?: { "paths-ignore"?: string[]; paths?: string[] }; + pull_request?: { "paths-ignore"?: string[]; paths?: string[] }; +}; + +type Workflow = { on: WorkflowEvents }; + +function loadWorkflow(name: string): Workflow { + const raw = readFileSync(join(WORKFLOWS_DIR, name), "utf8"); + return load(raw) as Workflow; +} + +function sortedPaths(list: string[] | undefined): string[] { + return [...(list ?? [])].sort(); +} + +const CI = loadWorkflow("ci.yml"); +const CI_SKIP = loadWorkflow("ci-skip.yml"); +const CODEQL = loadWorkflow("codeql.yml"); +const CODEQL_SKIP = loadWorkflow("codeql-skip.yml"); + +describe("workflow paths-mirror", () => { + describe("ci pair (ci.yml + ci-skip.yml)", () => { + it("ci.yml: push.paths-ignore === pull_request.paths-ignore", () => { + expect(sortedPaths(CI.on.push?.["paths-ignore"])).toEqual( + sortedPaths(CI.on.pull_request?.["paths-ignore"]), + ); + }); + + it("ci-skip.yml: push.paths === pull_request.paths", () => { + expect(sortedPaths(CI_SKIP.on.push?.paths)).toEqual( + sortedPaths(CI_SKIP.on.pull_request?.paths), + ); + }); + + it("ci.yml paths-ignore === ci-skip.yml paths (補完 pair 整合)", () => { + expect(sortedPaths(CI.on.pull_request?.["paths-ignore"])).toEqual( + sortedPaths(CI_SKIP.on.pull_request?.paths), + ); + }); + }); + + describe("codeql pair (codeql.yml + codeql-skip.yml)", () => { + it("codeql.yml: push.paths-ignore === pull_request.paths-ignore", () => { + expect(sortedPaths(CODEQL.on.push?.["paths-ignore"])).toEqual( + sortedPaths(CODEQL.on.pull_request?.["paths-ignore"]), + ); + }); + + it("codeql.yml paths-ignore === codeql-skip.yml paths (補完 pair 整合)", () => { + expect(sortedPaths(CODEQL.on.pull_request?.["paths-ignore"])).toEqual( + sortedPaths(CODEQL_SKIP.on.pull_request?.paths), + ); + }); + }); + + describe("全 pair 統一 pattern", () => { + it("4 workflow の path 定義がすべて同一", () => { + const canonical = sortedPaths(CI.on.pull_request?.["paths-ignore"]); + expect(canonical).not.toHaveLength(0); + expect(sortedPaths(CI_SKIP.on.pull_request?.paths)).toEqual(canonical); + expect(sortedPaths(CODEQL.on.pull_request?.["paths-ignore"])).toEqual(canonical); + expect(sortedPaths(CODEQL_SKIP.on.pull_request?.paths)).toEqual(canonical); + }); + }); +}); diff --git a/tsconfig.node.json b/tsconfig.node.json index 456babd..d8de407 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -20,5 +20,5 @@ "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true }, - "include": ["electron", "electron.vite.config.ts", "src/types", "src/lib/lru-cache.ts"] + "include": ["electron", "electron.vite.config.ts", "src/types", "src/lib/lru-cache.ts", "test"] } diff --git a/vitest.config.ts b/vitest.config.ts index 22e7ac4..2ac3fe6 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -29,6 +29,15 @@ export default defineConfig({ exclude: [...configDefaults.exclude, "e2e/**", "out/**"], }, }, + { + extends: true, + test: { + name: "workflows", + environment: "node", + include: ["test/**/*.test.ts"], + exclude: [...configDefaults.exclude, "e2e/**", "out/**"], + }, + }, ], }, });