From 408c5996e2e510a9de0714822ccd78b22b97cadb Mon Sep 17 00:00:00 2001 From: Dotta Date: Sat, 5 Sep 2026 08:50:45 -0500 Subject: [PATCH] fix(connections): preserve all-repository warning Co-Authored-By: Paperclip --- ui/src/pages/apps/AppDetail.test.tsx | 82 ++++++++++++++----- .../apps/app-detail/IdentitiesSection.tsx | 15 ++-- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/ui/src/pages/apps/AppDetail.test.tsx b/ui/src/pages/apps/AppDetail.test.tsx index ff14888a4c..6a9f427424 100644 --- a/ui/src/pages/apps/AppDetail.test.tsx +++ b/ui/src/pages/apps/AppDetail.test.tsx @@ -262,6 +262,32 @@ function personalGrant(overrides: Record = {}) { }; } +function dedicatedGitHubGrant(overrides: Record = {}) { + return organizationGrant({ + id: "grant-agent", + kind: "agent", + subjectAgentId: "agent-1", + subjectUserId: null, + isDefault: false, + providerTenant: { + github: { + userId: "123", + login: "dottabot", + installationCount: 1, + repositoryCount: 1, + repositorySelection: "selected", + installationIds: ["456"], + installationOwnerLogins: ["paperclipai"], + managementUrl: "https://github.com/settings/installations/456", + webhookHealth: "pending", + lastWebhookAt: null, + lastAccessRefreshAt: "2026-09-05T12:00:00.000Z", + }, + }, + ...overrides, + }); +} + function catalogEntry(overrides: Record = {}) { return { id: "catalog-read", @@ -1433,28 +1459,7 @@ describe("AppDetail", () => { })); listConnectionGrantsMock.mockResolvedValue({ connection: { id: "conn-1", uid: "conn-1" }, - grants: [organizationGrant({ - id: "grant-agent", - kind: "agent", - subjectAgentId: "agent-1", - subjectUserId: null, - isDefault: false, - providerTenant: { - github: { - userId: "123", - login: "dottabot", - installationCount: 1, - repositoryCount: 1, - repositorySelection: "selected", - installationIds: ["456"], - installationOwnerLogins: ["paperclipai"], - managementUrl: "https://github.com/settings/installations/456", - webhookHealth: "pending", - lastWebhookAt: null, - lastAccessRefreshAt: "2026-09-05T12:00:00.000Z", - }, - }, - })], + grants: [dedicatedGitHubGrant()], capabilities: fullCapabilities(), currentUserId: "user-1", members: [], @@ -1476,6 +1481,39 @@ describe("AppDetail", () => { expect(container.textContent).not.toContain("Last access refresh"); }); + it("warns about all-repository GitHub access within the repository row", async () => { + mockParams.tab = "permissions"; + getConnectionMock.mockResolvedValue(connection({ + credentialPolicy: "per_agent", + authKind: "oauth", + })); + listConnectionGrantsMock.mockResolvedValue({ + connection: { id: "conn-1", uid: "conn-1" }, + grants: [dedicatedGitHubGrant({ + providerTenant: { + github: { + userId: "123", + login: "dottabot", + installationCount: 1, + repositoryCount: 0, + repositorySelection: "all", + installationIds: ["456"], + installationOwnerLogins: ["paperclipai"], + managementUrl: "https://github.com/settings/installations/456", + }, + }, + })], + capabilities: fullCapabilities(), + currentUserId: "user-1", + members: [], + }); + + await renderAppDetail(); + + expect(container.textContent).toContain("All current and future repositories"); + expect(container.textContent).not.toContain("selected repositories"); + }); + it("persists an empty audience as all organization members", async () => { mockParams.tab = "permissions"; getConnectionMock.mockResolvedValue(connection({ createdByUserId: "user-1" })); diff --git a/ui/src/pages/apps/app-detail/IdentitiesSection.tsx b/ui/src/pages/apps/app-detail/IdentitiesSection.tsx index bc055ed74c..1e04918ef8 100644 --- a/ui/src/pages/apps/app-detail/IdentitiesSection.tsx +++ b/ui/src/pages/apps/app-detail/IdentitiesSection.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState, type ReactNode } from "react"; -import { Building2, Loader2, UserRound } from "lucide-react"; +import { Building2, Loader2, TriangleAlert, UserRound } from "lucide-react"; import type { ConnectionAudienceMember, ConnectionGrant, @@ -296,15 +296,20 @@ function GitHubConnectionSummary({ }) { const github = grant.providerTenant?.github; if (!github) return null; - const repositorySummary = github.repositorySelection === "all" - ? "All repositories" - : `${github.repositoryCount} selected repositories`; + const allRepositories = github.repositorySelection === "all"; return (
Repositories
-
{repositorySummary}
+ {allRepositories ? ( +
+ + All current and future repositories +
+ ) : ( +
{github.repositoryCount} selected repositories
+ )}
{github.managementUrl ? (