From 4faba18dd566854da295a234adb706643e1995f0 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 14 Apr 2026 13:21:41 +1000 Subject: [PATCH 1/2] fix: resolve vitest hang caused by mkdirSync on /proc The JS test suite hung indefinitely because safe_output_manifest.test.cjs called writeTemporaryIdMapFile({}, "/proc/fake/map.json") which internally runs fs.mkdirSync('/proc/fake', { recursive: true }). This hangs on Linux because the procfs filesystem blocks the recursive mkdir call. Changes: - Replace /proc/fake/map.json with /dev/null/fake/map.json which fails immediately (ENOTDIR) since /dev/null is a file, not a directory - Remove forceExit: true from vitest.config.mjs (added by #26143 as a workaround, but it masked the real issue rather than fixing it) --- actions/setup/js/safe_output_manifest.test.cjs | 4 ++-- actions/setup/js/vitest.config.mjs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/actions/setup/js/safe_output_manifest.test.cjs b/actions/setup/js/safe_output_manifest.test.cjs index e8ebfc97137..3e0fe707646 100644 --- a/actions/setup/js/safe_output_manifest.test.cjs +++ b/actions/setup/js/safe_output_manifest.test.cjs @@ -371,8 +371,8 @@ describe("safe_output_manifest", () => { }); it("should throw when the file cannot be written", () => { - // Use a path that cannot be created (root filesystem) - expect(() => writeTemporaryIdMapFile({}, "/proc/fake/map.json")).toThrow("Failed to write temporary ID map file"); + // Use a path under /dev/null which is a file, not a directory — mkdirSync fails immediately + expect(() => writeTemporaryIdMapFile({}, "/dev/null/fake/map.json")).toThrow("Failed to write temporary ID map file"); }); }); }); diff --git a/actions/setup/js/vitest.config.mjs b/actions/setup/js/vitest.config.mjs index dcfc85e47b1..39ab29e632f 100644 --- a/actions/setup/js/vitest.config.mjs +++ b/actions/setup/js/vitest.config.mjs @@ -7,7 +7,6 @@ export default defineConfig({ include: ["**/*.test.{js,cjs}"], testTimeout: 10000, hookTimeout: 10000, - forceExit: true, coverage: { provider: "v8", reporter: ["text", "html"], From e56699ee9374ed34bc10f7af38088b16b6355e3c Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 14 Apr 2026 13:42:28 +1000 Subject: [PATCH 2/2] fix tests --- actions/setup/js/checkout_pr_branch.test.cjs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/actions/setup/js/checkout_pr_branch.test.cjs b/actions/setup/js/checkout_pr_branch.test.cjs index 8ea1d240bfd..224f3b01cb0 100644 --- a/actions/setup/js/checkout_pr_branch.test.cjs +++ b/actions/setup/js/checkout_pr_branch.test.cjs @@ -372,7 +372,7 @@ If the pull request is still open, verify that: await runScript(); // Fork status should be "unknown" initially (minimal PR object) - expect(mockCore.info).toHaveBeenCalledWith("Is fork PR: unknown (PR details not available in event payload)"); + expect(mockCore.info).toHaveBeenCalledWith("Is fork PR: unknown (head/base repo details not available in event payload)"); // After API call, fork status should be resolved expect(mockCore.info).toHaveBeenCalledWith("Is fork PR (from API): false (same repository)"); // Should NOT emit fork warning for a non-fork PR @@ -404,7 +404,7 @@ If the pull request is still open, verify that: await runScript(); // Fork status should be "unknown" initially, then resolved from API - expect(mockCore.info).toHaveBeenCalledWith("Is fork PR: unknown (PR details not available in event payload)"); + expect(mockCore.info).toHaveBeenCalledWith("Is fork PR: unknown (head/base repo details not available in event payload)"); expect(mockCore.info).toHaveBeenCalledWith("Is fork PR (from API): true (different repository names)"); // Should emit fork warning expect(mockCore.warning).toHaveBeenCalledWith("⚠️ Fork PR detected - fetching via refs/pull/N/head from origin"); @@ -616,11 +616,24 @@ If the pull request is still open, verify that: // Simulate deleted fork scenario delete mockContext.payload.pull_request.head.repo; + // fetchPRDetails returns full PR data with deleted head repo + mockGithub.rest.pulls.get.mockResolvedValueOnce({ + data: { + state: "open", + commits: 1, + head: { ref: "feature-branch", repo: null }, + base: { ref: "main", repo: { full_name: "test-owner/test-repo", owner: { login: "test-owner" } } }, + }, + }); + await runScript(); // Verify deleted fork detection expect(mockCore.warning).toHaveBeenCalledWith("⚠️ Head repo information not available (repo may be deleted)"); - expect(mockCore.info).toHaveBeenCalledWith("Is fork PR: true (head repository deleted (was likely a fork))"); + // logPRContext reports unknown because head.repo is missing in the payload + expect(mockCore.info).toHaveBeenCalledWith("Is fork PR: unknown (head/base repo details not available in event payload)"); + // After API call, fork status is resolved via detectForkPR + expect(mockCore.info).toHaveBeenCalledWith("Is fork PR (from API): true (head repository deleted (was likely a fork))"); expect(mockCore.warning).toHaveBeenCalledWith("⚠️ Fork PR detected - fetching via refs/pull/N/head from origin"); });