Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/github/backfill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1193,7 +1193,12 @@ async function refreshInstallationHealthRecords(env: Env, installations: Install
const installedRepos = repositories.filter((repo) => repo.installationId === currentInstallation.id && repo.isInstalled);
const registeredInstalled = installedRepos.filter((repo) => repo.isRegistered);
const installedSettings = await Promise.all(installedRepos.map((repo) => resolveRepositorySettings(env, repo.fullName)));
const requiresChecks = installedSettings.some((settings) => settings.checkRunMode === "enabled");
// #5355: also require Checks: write when the review-agent check-run (reviewCheckMode) publishes, not
// just when the separate context check (checkRunMode) is enabled -- buildInstallationRepairDiagnostics
// above already ORs both (checkRunRepoCount / gateCheckRepoCount); this persisted health record was
// missing the second arm, so an installation with only reviewCheckMode set never got flagged for the
// Checks permission it actually needs.
const requiresChecks = installedSettings.some((settings) => settings.checkRunMode === "enabled" || shouldPublishReviewCheck(settings.reviewCheckMode));
const requiresPrWrite = installedSettings.some((settings) => agentRequiresPrWrite(settings.autonomy));
const requiresContentsWrite = installedSettings.some((settings) => agentRequiresContentsWrite(settings.autonomy));
const requiredPermissions = {
Expand Down
49 changes: 49 additions & 0 deletions test/unit/backfill.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,55 @@ describe("GitHub backfill", () => {
);
});

it("REGRESSION (#5355): requires Checks write for a repo with only reviewCheckMode (the Orb Review Agent check) set, not checkRunMode", async () => {
// Before the fix, requiresChecks only looked at checkRunMode ("Gittensory Context" check) and missed
// the separate reviewCheckMode axis ("Gittensory Orb Review Agent" check) entirely -- so an installation
// whose repos only ever published the review-agent check (true for JSONbored's own 3 production repos,
// none of which set checkRunMode) was never flagged as needing the Checks permission.
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
await seedRegisteredRepo(env);
await upsertInstallation(env, {
installation: {
id: 123,
account: { login: "JSONbored", id: 1, type: "User" },
repository_selection: "selected",
permissions: { metadata: "read", pull_requests: "write", issues: "write" },
events: ["issues", "issue_comment", "pull_request", "repository", "installation_repositories"],
},
});
await upsertRepositoryFromGitHub(env, { name: "gittensory", full_name: "JSONbored/gittensory", private: true, owner: { login: "JSONbored" } }, 123);
await upsertRepositorySettings(env, {
repoFullName: "JSONbored/gittensory",
reviewCheckMode: "required", // checkRunMode left at its default ("off")
});
vi.stubGlobal("fetch", async (input: RequestInfo | URL) => {
const url = input.toString();
if (url.endsWith("/app/installations/123")) {
return Response.json({
id: 123,
account: { login: "JSONbored", id: 1, type: "User" },
repository_selection: "selected",
permissions: { metadata: "read", pull_requests: "write", issues: "write" },
events: ["issues", "issue_comment", "pull_request", "repository", "installation_repositories"],
});
}
return new Response("not found", { status: 404 });
});

const refreshed = await refreshInstallationHealth(env);

expect(refreshed.installations).toEqual(
expect.arrayContaining([
expect.objectContaining({
installationId: 123,
status: "needs_attention", // was falsely "healthy" before the fix
missingPermissions: ["checks"],
requiredPermissions: expect.objectContaining({ checks: "write" }),
}),
]),
);
});

it("REGRESSION (#audit-install-health): an acting autonomy requires pull_requests:write, so read-only is needs_attention not healthy", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
await seedRegisteredRepo(env);
Expand Down