diff --git a/frontend/src/lib/components/GitHubAppIntegration.svelte b/frontend/src/lib/components/GitHubAppIntegration.svelte index 7aa136b072..11844d7d9c 100644 --- a/frontend/src/lib/components/GitHubAppIntegration.svelte +++ b/frontend/src/lib/components/GitHubAppIntegration.svelte @@ -55,6 +55,19 @@ ) ) + // Org names that appear on more than one installation, so the dropdown can + // tell those entries apart. + let duplicatedAccountIds = $derived( + new Set( + githubState.workspaceGithubInstallations + .filter( + (installation, _, array) => + array.filter((other) => other.account_id === installation.account_id).length > 1 + ) + .map((installation) => installation.account_id) + ) + ) + let showGitHubApp = $derived( resourceType === 'git_repository' && $workspaceStore && @@ -205,29 +218,47 @@

GitHub Account ID

- (githubState.selectedGHAppRepository = undefined)} + > + {#each githubState.workspaceGithubInstallations as installation (`select-${installation.installation_id}-${installation.workspace_id}`)} - {/each}
- {#if githubState.selectedGHAppAccountId} + {#if githubState.selectedGHAppInstallationId !== undefined} {@const selectedInstallation = githubState.workspaceGithubInstallations.find( - (inst) => inst.account_id === githubState.selectedGHAppAccountId + (inst) => inst.installation_id === githubState.selectedGHAppInstallationId )} {#if selectedInstallation}

Repository

- + + {#key selectedInstallation.installation_id} + + {/key}
{/if} {/if} diff --git a/frontend/src/lib/components/RepositorySelector.svelte b/frontend/src/lib/components/RepositorySelector.svelte index cfeaa3945d..30f8361cbb 100644 --- a/frontend/src/lib/components/RepositorySelector.svelte +++ b/frontend/src/lib/components/RepositorySelector.svelte @@ -11,7 +11,7 @@ interface Props { disabled?: boolean selectedRepository?: string | undefined - accountId: string + installationId: number initialRepositories: Repository[] totalCount: number perPage: number @@ -23,7 +23,7 @@ let { disabled = false, selectedRepository = $bindable(), - accountId, + installationId, initialRepositories, totalCount, perPage, @@ -67,8 +67,8 @@ page: nextPage }) - // Find the matching installation and get its repositories - const installation = installations.find((inst) => inst.account_id === accountId) + // Match on installation_id: several installations can share one account_id + const installation = installations.find((inst) => inst.installation_id === installationId) if (installation?.repositories) { // Append new repos to existing ones @@ -95,8 +95,8 @@ }))} placeholder="Select repository..." clearable - disabled={disabled} - bind:filterText={filterText} + {disabled} + bind:filterText bind:value={selectedRepository} /> {#if hasMoreRepos} diff --git a/frontend/src/lib/githubApp.ts b/frontend/src/lib/githubApp.ts index f60b1f8538..666734546e 100644 --- a/frontend/src/lib/githubApp.ts +++ b/frontend/src/lib/githubApp.ts @@ -10,7 +10,12 @@ export interface GitHubAppState { loadingGithubInstallations: boolean githubInstallations: GetGlobalConnectedRepositoriesResponse workspaceGithubInstallations: GetGlobalConnectedRepositoriesResponse - selectedGHAppAccountId: string | undefined + /** + * Installations are keyed by `installation_id`, never by `account_id`: an org + * can be installed more than once (re-installed, or added from another + * workspace), and matching on the org name resolves to the wrong one. + */ + selectedGHAppInstallationId: number | undefined selectedGHAppRepository: string | undefined githubInstallationUrl: string | undefined installationCheckInterval: number | undefined @@ -25,11 +30,6 @@ export interface GitHubAppState { isGhesSelfManaged: boolean } -export interface GitHubRepository { - name: string - url: string -} - export interface GitHubAppError extends Error { code: 'VALIDATION_ERROR' | 'NETWORK_ERROR' | 'AUTH_ERROR' | 'UNKNOWN_ERROR' details?: unknown @@ -101,7 +101,7 @@ export function createGitHubAppState(): GitHubAppState { loadingGithubInstallations: false, githubInstallations: [], workspaceGithubInstallations: [], - selectedGHAppAccountId: undefined, + selectedGHAppInstallationId: undefined, selectedGHAppRepository: undefined, githubInstallationUrl: undefined, installationCheckInterval: undefined, @@ -241,18 +241,6 @@ export function stopInstallationCheck(state: GitHubAppState): void { state.isCheckingInstallation = false } -/** - * Gets repositories for a specific GitHub account - */ -export function getRepositories(state: GitHubAppState, accountId: string): GitHubRepository[] { - if (!accountId) return [] - - return ( - state.githubInstallations.find((installation) => installation.account_id === accountId) - ?.repositories || [] - ) -} - /** * Adds a GitHub installation to the current workspace */