diff --git a/action/index.mjs b/action/index.mjs index 6881a36..6fe0a41 100644 --- a/action/index.mjs +++ b/action/index.mjs @@ -230025,7 +230025,9 @@ async function fetchRulesets(client, basePath, budget) { `${basePath}?per_page=${PER_PAGE3}&page=${page}` ); } catch (err) { - if (err instanceof Error && err.message.includes("404")) return []; + if (err instanceof Error && (err.message.includes("404") || err.message.includes("403"))) { + return []; + } throw err; } if (!Array.isArray(batch) || batch.length === 0) break; diff --git a/src/cycles/rulesets.test.ts b/src/cycles/rulesets.test.ts index e0fc5bd..89bea43 100644 --- a/src/cycles/rulesets.test.ts +++ b/src/cycles/rulesets.test.ts @@ -137,6 +137,16 @@ describe("fetchRulesets", () => { expect(live).toEqual([]); }); + it("returns empty on a 403 (App lacks permission for this rulesets scope)", async () => { + const client: MockClient = makeMockClient(); + client.request = async (method: string, path: string): Promise => { + client.calls.push({ method, path }); + throw new Error("GET /orgs/test-org/rulesets returned 403: Resource not accessible by integration"); + }; + const live = await fetchRulesets(client, "/orgs/test-org/rulesets", makeBudget()); + expect(live).toEqual([]); + }); + it("charges the budget: one list page + one detail per ruleset", async () => { const client = makeMockClient({ "GET /orgs/test-org/rulesets?per_page=100&page=1": [ diff --git a/src/cycles/rulesets.ts b/src/cycles/rulesets.ts index 7d3d16e..fafa125 100644 --- a/src/cycles/rulesets.ts +++ b/src/cycles/rulesets.ts @@ -82,7 +82,9 @@ const PER_PAGE = 100; * `/repos/{o}/{r}/rulesets`): list (paginated) then GET each ruleset's detail * so `rules`/`conditions`/`bypassActors` are populated for diffing. Charges the * budget per request and stops when exhausted. A 404 (rulesets unsupported / - * repo missing) yields an empty list. + * repo missing) or 403 (the App lacks permission for this rulesets scope — e.g. + * org rulesets) yields an empty list rather than aborting the cycle, so an + * inaccessible org-rulesets read never blocks repo-rulesets reconciliation. */ export async function fetchRulesets( client: AppClient, @@ -102,7 +104,9 @@ export async function fetchRulesets( `${basePath}?per_page=${PER_PAGE}&page=${page}`, ); } catch (err) { - if (err instanceof Error && err.message.includes("404")) return []; + if (err instanceof Error && (err.message.includes("404") || err.message.includes("403"))) { + return []; + } throw err; } if (!Array.isArray(batch) || batch.length === 0) break;