mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-05 16:03:47 +00:00
fix(frontend): key the GitHub App installation selector on installation_id (#10831)
* fix(frontend): key the GitHub App installation selector on installation_id The GitHub Account ID dropdown used `account_id` as both the option value and the lookup key. A workspace can hold several installations for the same org (re-installed, or added from another workspace), so `.find()` resolved to whichever came first: picking the live installation could hand back a stale, token-errored one whose `repositories` are empty, leaving the repository dropdown blank. `RepositorySelector`'s pagination matched the same way and appended the wrong installation's page. Both now key on `installation_id`, and the dropdown appends the installation id to the label only for orgs that appear more than once. Switching installation remounts `RepositorySelector` and clears the selected repository, so its loaded pages no longer carry over. Fixes WIN-2448 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(frontend): shorten the duplicate-org installation label Drop the "installation" word from the disambiguating suffix: the id alone already tells the two entries apart, and it keeps the errored variant short enough to read at a glance. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6b73145e72
commit
78331fda8b
@@ -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 @@
|
||||
<div class="flex flex-row gap-2 w-full">
|
||||
<div class="flex flex-col gap-1 flex-1">
|
||||
<p class="text-sm font-semibold text-secondary">GitHub Account ID</p>
|
||||
<select bind:value={githubState.selectedGHAppAccountId}>
|
||||
<option value="" disabled>Select GitHub Account ID</option>
|
||||
<select
|
||||
bind:value={githubState.selectedGHAppInstallationId}
|
||||
onchange={() => (githubState.selectedGHAppRepository = undefined)}
|
||||
>
|
||||
<option value={undefined} disabled>Select GitHub Account ID</option>
|
||||
{#each githubState.workspaceGithubInstallations as installation (`select-${installation.installation_id}-${installation.workspace_id}`)}
|
||||
<option value={installation.account_id} disabled={!!installation.error}>
|
||||
{installation.account_id}{installation.error ? ' (token error)' : ''}
|
||||
{@const details = [
|
||||
duplicatedAccountIds.has(installation.account_id)
|
||||
? `${installation.installation_id}`
|
||||
: undefined,
|
||||
installation.error ? 'token error' : undefined
|
||||
].filter(Boolean)}
|
||||
<option
|
||||
value={installation.installation_id}
|
||||
disabled={!!installation.error}
|
||||
>
|
||||
{installation.account_id}{details.length
|
||||
? ` (${details.join(', ')})`
|
||||
: ''}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
{#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}
|
||||
<div class="flex flex-col gap-1 flex-1">
|
||||
<p class="text-sm font-semibold text-secondary">Repository</p>
|
||||
<RepositorySelector
|
||||
bind:selectedRepository={githubState.selectedGHAppRepository}
|
||||
accountId={githubState.selectedGHAppAccountId}
|
||||
initialRepositories={selectedInstallation.repositories}
|
||||
totalCount={selectedInstallation.total_count}
|
||||
perPage={selectedInstallation.per_page}
|
||||
/>
|
||||
<!-- RepositorySelector snapshots its repositories and page cursor at
|
||||
mount, so switching installation has to remount it. -->
|
||||
{#key selectedInstallation.installation_id}
|
||||
<RepositorySelector
|
||||
bind:selectedRepository={githubState.selectedGHAppRepository}
|
||||
installationId={selectedInstallation.installation_id}
|
||||
initialRepositories={selectedInstallation.repositories}
|
||||
totalCount={selectedInstallation.total_count}
|
||||
perPage={selectedInstallation.per_page}
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user