





















11// Check Workflows tests cover check workflows script behavior.
22import { spawnSync } from "node:child_process";
3-import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4-import os from "node:os";
3+import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
54import path from "node:path";
6-import { describe, expect, it } from "vitest";
5+import { afterEach, describe, expect, it } from "vitest";
6+import { cleanupTempDirs, makeTempDir } from "../helpers/temp-dir.js";
7788const scriptPath = path.resolve("scripts/check-workflows.mjs");
9+const tempDirs: string[] = [];
10+11+afterEach(() => {
12+cleanupTempDirs(tempDirs);
13+});
9141015describe("check-workflows", () => {
1116it("prints an actionable diagnostic when actionlint and go are unavailable", () => {
@@ -23,117 +28,186 @@ describe("check-workflows", () => {
2328});
24292530it("uses the pinned go fallback and audits all workflows with zizmor", () => {
26-const tempDir = mkdtempSync(path.join(os.tmpdir(), "check-workflows-"));
27-try {
28-const binDir = path.join(tempDir, "bin");
29-const markerPath = path.join(tempDir, "go-run.txt");
30-const preCommitMarkerPath = path.join(tempDir, "pre-commit.txt");
31-mkdirSync(binDir);
32-writeFileSync(
33-path.join(binDir, "go"),
34-[
35-"#!/bin/sh",
36-'if [ "$1" = "version" ]; then exit 0; fi',
37-'if [ "$1" = "run" ]; then printf "%s\\n" "$*" > "$GO_FALLBACK_MARKER"; exit 0; fi',
38-"exit 1",
39-"",
40-].join("\n"),
41-{ mode: 0o755 },
42-);
43-writeFileSync(
44-path.join(binDir, "pre-commit"),
45-[
46-"#!/bin/sh",
47-'if [ "$1" = "--version" ]; then exit 0; fi',
48-'printf "%s\\n" "$*" >> "$PRE_COMMIT_MARKER"',
49-"exit 0",
50-"",
51-].join("\n"),
52-{ mode: 0o755 },
53-);
54-for (const command of ["python3", "node"]) {
55-writeFileSync(path.join(binDir, command), "#!/bin/sh\nexit 0\n", { mode: 0o755 });
56-}
57-58-const result = spawnSync(process.execPath, [scriptPath], {
59-encoding: "utf8",
60-env: {
61- ...process.env,
62-GO_FALLBACK_MARKER: markerPath,
63-PRE_COMMIT_MARKER: preCommitMarkerPath,
64-PATH: binDir,
65-},
66-});
67-68-expect(result.status).toBe(0);
69-expect(readFileSync(markerPath, "utf8")).toContain(
70-"github.com/rhysd/actionlint/cmd/actionlint@v1.7.11",
71-);
72-const preCommitArgs = readFileSync(preCommitMarkerPath, "utf8");
73-expect(preCommitArgs).toContain("run --config .pre-commit-config.yaml zizmor --files");
74-expect(preCommitArgs).toContain(".github/workflows/ci.yml");
75-expect(preCommitArgs).toContain(".github/workflows/windows-testbox-probe.yml");
76-} finally {
77-rmSync(tempDir, { force: true, recursive: true });
31+const tempDir = makeTempDir(tempDirs, "check-workflows-");
32+const binDir = path.join(tempDir, "bin");
33+const markerPath = path.join(tempDir, "go-run.txt");
34+const preCommitMarkerPath = path.join(tempDir, "pre-commit.txt");
35+mkdirSync(binDir);
36+writeFileSync(
37+path.join(binDir, "go"),
38+[
39+"#!/bin/sh",
40+'if [ "$1" = "version" ]; then exit 0; fi',
41+'if [ "$1" = "run" ]; then printf "%s\\n" "$*" > "$GO_FALLBACK_MARKER"; exit 0; fi',
42+"exit 1",
43+"",
44+].join("\n"),
45+{ mode: 0o755 },
46+);
47+writeFileSync(
48+path.join(binDir, "pre-commit"),
49+[
50+"#!/bin/sh",
51+'if [ "$1" = "--version" ]; then exit 0; fi',
52+'printf "%s\\n" "$*" >> "$PRE_COMMIT_MARKER"',
53+"exit 0",
54+"",
55+].join("\n"),
56+{ mode: 0o755 },
57+);
58+for (const command of ["python3", "node"]) {
59+writeFileSync(path.join(binDir, command), "#!/bin/sh\nexit 0\n", { mode: 0o755 });
7860}
61+62+const result = spawnSync(process.execPath, [scriptPath], {
63+encoding: "utf8",
64+env: {
65+ ...process.env,
66+GO_FALLBACK_MARKER: markerPath,
67+PRE_COMMIT_MARKER: preCommitMarkerPath,
68+PATH: binDir,
69+},
70+});
71+72+expect(result.status).toBe(0);
73+expect(readFileSync(markerPath, "utf8")).toContain(
74+"github.com/rhysd/actionlint/cmd/actionlint@v1.7.11",
75+);
76+const preCommitArgs = readFileSync(preCommitMarkerPath, "utf8");
77+expect(preCommitArgs).toContain("run --config .pre-commit-config.yaml zizmor --files");
78+expect(preCommitArgs).toContain(".github/workflows/ci.yml");
79+expect(preCommitArgs).toContain(".github/workflows/windows-testbox-probe.yml");
7980});
80818182it("bootstraps pinned pre-commit in a temporary Python venv when needed", () => {
82-const tempDir = mkdtempSync(path.join(os.tmpdir(), "check-workflows-"));
83-try {
84-const binDir = path.join(tempDir, "bin");
85-const markerPath = path.join(tempDir, "python.txt");
86-mkdirSync(binDir);
87-writeFileSync(path.join(binDir, "node"), "#!/bin/sh\nexit 0\n", { mode: 0o755 });
88-writeFileSync(
89-path.join(binDir, "python3"),
90-[
91-"#!/bin/sh",
92-'if [ "$1" = "--version" ]; then exit 0; fi',
93-'if [ "$1" = "-m" ] && [ "$2" = "pre_commit" ] && [ "$3" = "--version" ]; then exit 1; fi',
94-'if [ "$1" = "-m" ] && [ "$2" = "pip" ]; then',
95-' printf "%s\\n" "$*" >> "$PRE_COMMIT_BOOTSTRAP_MARKER"',
96-" exit 0",
97-"fi",
98-'if [ "$1" = "-m" ] && [ "$2" = "pre_commit" ]; then',
99-' printf "%s\\n" "$*" >> "$PRE_COMMIT_BOOTSTRAP_MARKER"',
100-" exit 0",
101-"fi",
102-'if [ "$1" = "-m" ] && [ "$2" = "venv" ]; then',
103-' /bin/mkdir -p "$3/bin"',
104-' /bin/cp "$0" "$3/bin/python"',
105-' /bin/chmod +x "$3/bin/python"',
106-" exit 0",
107-"fi",
108-"exit 0",
109-"",
110-].join("\n"),
111-{ mode: 0o755 },
112-);
113-114-const result = spawnSync(process.execPath, [scriptPath], {
115-encoding: "utf8",
116-env: {
117- ...process.env,
118-PATH: binDir,
119-PRE_COMMIT_BOOTSTRAP_MARKER: markerPath,
120-},
121-});
122-123-expect(result.status).toBe(0);
124-const pythonArgs = readFileSync(markerPath, "utf8");
125-expect(pythonArgs).toContain(
126-"-m pip install --disable-pip-version-check pre-commit==4.2.0",
127-);
128-expect(pythonArgs).toContain(
129-"-m pre_commit run --config .pre-commit-config.yaml actionlint --files",
130-);
131-expect(pythonArgs).toContain(
132-"-m pre_commit run --config .pre-commit-config.yaml zizmor --files",
133-);
134-} finally {
135-rmSync(tempDir, { force: true, recursive: true });
136-}
83+const tempDir = makeTempDir(tempDirs, "check-workflows-");
84+const binDir = path.join(tempDir, "bin");
85+const markerPath = path.join(tempDir, "python.txt");
86+mkdirSync(binDir);
87+writeFileSync(path.join(binDir, "node"), "#!/bin/sh\nexit 0\n", { mode: 0o755 });
88+writeFileSync(
89+path.join(binDir, "python3"),
90+[
91+"#!/bin/sh",
92+'if [ "$1" = "--version" ]; then exit 0; fi',
93+'if [ "$1" = "-m" ] && [ "$2" = "pre_commit" ] && [ "$3" = "--version" ]; then exit 1; fi',
94+'if [ "$1" = "-m" ] && [ "$2" = "pip" ]; then',
95+' printf "%s\\n" "$*" >> "$PRE_COMMIT_BOOTSTRAP_MARKER"',
96+" exit 0",
97+"fi",
98+'if [ "$1" = "-m" ] && [ "$2" = "pre_commit" ]; then',
99+' printf "%s\\n" "$*" >> "$PRE_COMMIT_BOOTSTRAP_MARKER"',
100+" exit 0",
101+"fi",
102+'if [ "$1" = "-m" ] && [ "$2" = "venv" ]; then',
103+' /bin/mkdir -p "$3/bin"',
104+' /bin/cp "$0" "$3/bin/python"',
105+' /bin/chmod +x "$3/bin/python"',
106+" exit 0",
107+"fi",
108+"exit 0",
109+"",
110+].join("\n"),
111+{ mode: 0o755 },
112+);
113+114+const result = spawnSync(process.execPath, [scriptPath], {
115+encoding: "utf8",
116+env: {
117+ ...process.env,
118+PATH: binDir,
119+PRE_COMMIT_BOOTSTRAP_MARKER: markerPath,
120+},
121+});
122+123+expect(result.status).toBe(0);
124+const pythonArgs = readFileSync(markerPath, "utf8");
125+expect(pythonArgs).toContain("-m pip install --disable-pip-version-check pre-commit==4.2.0");
126+expect(pythonArgs).toContain(
127+"-m pre_commit run --config .pre-commit-config.yaml actionlint --files",
128+);
129+expect(pythonArgs).toContain(
130+"-m pre_commit run --config .pre-commit-config.yaml zizmor --files",
131+);
132+});
133+134+it("prints the missing runtime diagnostic when Python venv support is unavailable", () => {
135+const tempDir = makeTempDir(tempDirs, "check-workflows-");
136+const binDir = path.join(tempDir, "bin");
137+mkdirSync(binDir);
138+writeFileSync(path.join(binDir, "node"), "#!/bin/sh\nexit 0\n", { mode: 0o755 });
139+writeFileSync(
140+path.join(binDir, "python3"),
141+[
142+"#!/bin/sh",
143+'if [ "$1" = "--version" ]; then exit 0; fi',
144+'if [ "$1" = "-m" ] && [ "$2" = "pre_commit" ] && [ "$3" = "--version" ]; then exit 1; fi',
145+'if [ "$1" = "-m" ] && [ "$2" = "venv" ]; then',
146+' printf "%s\\n" "python venv unavailable" >&2',
147+" exit 1",
148+"fi",
149+"exit 1",
150+"",
151+].join("\n"),
152+{ mode: 0o755 },
153+);
154+155+const result = spawnSync(process.execPath, [scriptPath], {
156+encoding: "utf8",
157+env: {
158+ ...process.env,
159+PATH: binDir,
160+},
161+});
162+163+expect(result.status).toBe(1);
164+expect(result.stderr).toContain("python venv unavailable");
165+expect(result.stderr).toContain("missing pre-commit runtime for actionlint");
166+expect(result.stderr).toContain("Python venv support for pre-commit 4.2.0");
167+});
168+169+it("cleans the temporary Python venv before exiting on hook failure", () => {
170+const tempDir = makeTempDir(tempDirs, "check-workflows-");
171+const binDir = path.join(tempDir, "bin");
172+const markerPath = path.join(tempDir, "venv-path.txt");
173+mkdirSync(binDir);
174+writeFileSync(path.join(binDir, "node"), "#!/bin/sh\nexit 0\n", { mode: 0o755 });
175+writeFileSync(
176+path.join(binDir, "python3"),
177+[
178+"#!/bin/sh",
179+'if [ "$1" = "--version" ]; then exit 0; fi',
180+'if [ "$1" = "-m" ] && [ "$2" = "pre_commit" ] && [ "$3" = "--version" ]; then exit 1; fi',
181+'if [ "$1" = "-m" ] && [ "$2" = "venv" ]; then',
182+' /bin/mkdir -p "$3/bin"',
183+' /bin/cp "$0" "$3/bin/python"',
184+' /bin/chmod +x "$3/bin/python"',
185+' printf "%s\\n" "$3" > "$PRE_COMMIT_VENV_MARKER"',
186+" exit 0",
187+"fi",
188+'if [ "$1" = "-m" ] && [ "$2" = "pip" ]; then exit 0; fi',
189+'if [ "$1" = "-m" ] && [ "$2" = "pre_commit" ]; then',
190+' printf "%s\\n" "hook failed" >&2',
191+" exit 13",
192+"fi",
193+"exit 1",
194+"",
195+].join("\n"),
196+{ mode: 0o755 },
197+);
198+199+const result = spawnSync(process.execPath, [scriptPath], {
200+encoding: "utf8",
201+env: {
202+ ...process.env,
203+PATH: binDir,
204+PRE_COMMIT_VENV_MARKER: markerPath,
205+},
206+});
207+208+expect(result.status).toBe(13);
209+expect(result.stderr).toContain("hook failed");
210+expect(existsSync(readFileSync(markerPath, "utf8").trim())).toBe(false);
137211});
138212139213it("keeps Windows WSL2 probe output normalized through the shared wrapper", () => {
@@ -151,7 +225,9 @@ describe("check-workflows", () => {
151225expect(workflow).toContain("wsl2_restart_required=true");
152226expect(workflow).toContain("import_ubuntu_wsl2=skipped_restart_required");
153227expect(workflow).toContain("wsl_exec_skipped=restart_required");
154-expect(workflow).toContain('"wsl2_restart_required=$($restartRequired.ToString().ToLowerInvariant())"');
228+expect(workflow).toContain(
229+'"wsl2_restart_required=$($restartRequired.ToString().ToLowerInvariant())"',
230+);
155231expect(workflow).toContain(
156232'$exec = Invoke-WslText -Arguments @("-d", $distro, "--exec", "bash", "-lc"',
157233);
@@ -164,7 +240,9 @@ describe("check-workflows", () => {
164240const workflow = readFileSync(".github/workflows/windows-testbox-probe.yml", "utf8");
165241166242expect(workflow).toContain("run_windows_ci:");
167-expect(workflow).toContain('description: "Run the focused Windows-native CI test shard after probing"');
243+expect(workflow).toContain(
244+'description: "Run the focused Windows-native CI test shard after probing"',
245+);
168246expect(workflow).toContain("default: false");
169247expect(workflow).toContain("if: ${{ inputs.run_windows_ci }}");
170248expect(workflow).toContain("source .github/actions/setup-pnpm-store-cache/ensure-node.sh");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。