From 351b81b192c11270f56ccba0f03064cac1256648 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 15:17:03 +0000 Subject: [PATCH 1/3] Initial plan From aa795d3ebe90668f6aa335c0d9e538c251346661 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 15:27:30 +0000 Subject: [PATCH 2/3] Add retry logic for transient GitHub API errors in frontmatter hash test Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/error_recovery.cjs | 1 + actions/setup/js/error_recovery.test.cjs | 5 +++ .../js/frontmatter_hash_github_api.test.cjs | 35 +++++++++++++++++-- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/actions/setup/js/error_recovery.cjs b/actions/setup/js/error_recovery.cjs index d842c5923f2..8751ed4c402 100644 --- a/actions/setup/js/error_recovery.cjs +++ b/actions/setup/js/error_recovery.cjs @@ -54,6 +54,7 @@ function isTransientError(error) { "secondary rate limit", // GitHub secondary rate limits "abuse detection", // GitHub abuse detection "temporarily unavailable", + "no server is currently available", // GitHub API server unavailability ]; return transientPatterns.some(pattern => errorMsg.includes(pattern)); diff --git a/actions/setup/js/error_recovery.test.cjs b/actions/setup/js/error_recovery.test.cjs index 02d0b0bdb26..45c4bacf261 100644 --- a/actions/setup/js/error_recovery.test.cjs +++ b/actions/setup/js/error_recovery.test.cjs @@ -37,6 +37,11 @@ describe("error_recovery", () => { expect(isTransientError(new Error("Abuse detection triggered"))).toBe(true); }); + it("should identify GitHub server unavailability as transient", () => { + expect(isTransientError(new Error("No server is currently available to service your request"))).toBe(true); + expect(isTransientError(new Error("no server is currently available"))).toBe(true); + }); + it("should not identify validation errors as transient", () => { expect(isTransientError(new Error("Invalid input"))).toBe(false); expect(isTransientError(new Error("Field is required"))).toBe(false); diff --git a/actions/setup/js/frontmatter_hash_github_api.test.cjs b/actions/setup/js/frontmatter_hash_github_api.test.cjs index 63554be1f5d..4611aac45e1 100644 --- a/actions/setup/js/frontmatter_hash_github_api.test.cjs +++ b/actions/setup/js/frontmatter_hash_github_api.test.cjs @@ -1,8 +1,36 @@ // @ts-check -import { describe, it, expect, beforeAll } from "vitest"; +import { describe, it, expect, beforeAll, vi } from "vitest"; const path = require("path"); const fs = require("fs"); const { computeFrontmatterHash, createGitHubFileReader } = require("./frontmatter_hash_pure.cjs"); +const { withRetry, isTransientError } = require("./error_recovery.cjs"); + +// Mock @actions/core for retry logging in test environment +global.core = { + info: vi.fn((...args) => console.log(...args)), + warning: vi.fn((...args) => console.warn(...args)), + error: vi.fn((...args) => console.error(...args)), + debug: vi.fn((...args) => console.log(...args)), +}; + +/** + * Wraps a file reader function with retry logic for transient GitHub API errors + * @param {Function} fileReader - The original file reader function + * @returns {Function} File reader with retry logic + */ +function createRetryableFileReader(fileReader) { + return async function (filePath) { + return withRetry( + async () => fileReader(filePath), + { + maxRetries: 3, + initialDelayMs: 1000, + shouldRetry: isTransientError, + }, + `fetch file ${filePath}` + ); + }; +} /** * Tests for frontmatter hash computation using GitHub's API to fetch real workflows. @@ -354,7 +382,10 @@ describe("frontmatter_hash with GitHub API", () => { const ref = "main"; // Create file reader with real GitHub API - const fileReader = createGitHubFileReader(octokit, owner, repo, ref); + const baseFileReader = createGitHubFileReader(octokit, owner, repo, ref); + + // Wrap with retry logic to handle transient GitHub API errors + const fileReader = createRetryableFileReader(baseFileReader); // Test with a real public agentic workflow const workflowPath = ".github/workflows/audit-workflows.md"; From b097c7d7bed5de40d5c9ffb61c8598b32c111275 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 15:30:11 +0000 Subject: [PATCH 3/3] Address code review feedback: extract retry config constants and scope core mock Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../js/frontmatter_hash_github_api.test.cjs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/actions/setup/js/frontmatter_hash_github_api.test.cjs b/actions/setup/js/frontmatter_hash_github_api.test.cjs index 4611aac45e1..44ef183f09a 100644 --- a/actions/setup/js/frontmatter_hash_github_api.test.cjs +++ b/actions/setup/js/frontmatter_hash_github_api.test.cjs @@ -5,12 +5,11 @@ const fs = require("fs"); const { computeFrontmatterHash, createGitHubFileReader } = require("./frontmatter_hash_pure.cjs"); const { withRetry, isTransientError } = require("./error_recovery.cjs"); -// Mock @actions/core for retry logging in test environment -global.core = { - info: vi.fn((...args) => console.log(...args)), - warning: vi.fn((...args) => console.warn(...args)), - error: vi.fn((...args) => console.error(...args)), - debug: vi.fn((...args) => console.log(...args)), +// Retry configuration for live API tests +const LIVE_API_RETRY_CONFIG = { + maxRetries: 3, + initialDelayMs: 1000, + shouldRetry: isTransientError, }; /** @@ -20,15 +19,7 @@ global.core = { */ function createRetryableFileReader(fileReader) { return async function (filePath) { - return withRetry( - async () => fileReader(filePath), - { - maxRetries: 3, - initialDelayMs: 1000, - shouldRetry: isTransientError, - }, - `fetch file ${filePath}` - ); + return withRetry(async () => fileReader(filePath), LIVE_API_RETRY_CONFIG, `fetch file ${filePath}`); }; } @@ -41,6 +32,14 @@ describe("frontmatter_hash with GitHub API", () => { let mockGitHub; beforeAll(() => { + // Mock @actions/core for retry logging in test environment + global.core = { + info: vi.fn((...args) => console.log(...args)), + warning: vi.fn((...args) => console.warn(...args)), + error: vi.fn((...args) => console.error(...args)), + debug: vi.fn((...args) => console.log(...args)), + }; + // Create a mock GitHub API client for testing // In real scenarios, this would be replaced with @actions/github mockGitHub = {