Carry the project host on GitHub item dialog origins

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Jinjing
2026-07-20 12:35:26 -07:00
co-authored by Orca
parent b31cc77bd4
commit a6160e818b
8 changed files with 64 additions and 18 deletions
+9 -3
View File
@@ -134,13 +134,19 @@ vi.mock('./github-api-repository', async (importOriginal) => {
// Mirror production: dotcom origin slugs come back pinned to github.com.
return slug ? { host: 'github.com', ...slug } : slug
},
getIssueGitHubApiRepository: (repoPath: string, connectionId?: string | null) =>
getIssueOwnerRepoMock(repoPath, connectionId),
getIssueGitHubApiRepository: async (repoPath: string, connectionId?: string | null) => {
const slug = await getIssueOwnerRepoMock(repoPath, connectionId)
// Mirror production: issue slugs come back host-qualified to github.com.
return slug ? { host: 'github.com', ...slug } : slug
},
resolveIssueGitHubApiRepositorySource: async (
repoPath: string,
_preference: unknown,
connectionId?: string | null
) => ({ source: await getIssueOwnerRepoMock(repoPath, connectionId), fellBack: false })
) => {
const slug = await getIssueOwnerRepoMock(repoPath, connectionId)
return { source: slug ? { host: 'github.com', ...slug } : slug, fellBack: false }
}
}
})
+16
View File
@@ -65,6 +65,11 @@ export async function getIssue(
connectionId,
localGitOptions
)
// Why: a connection-backed request has no local cwd, so the non-GitHub
// fallback below would let gh target its default repository. Refuse instead.
if (connectionId && !ownerRepo) {
return null
}
await acquire()
try {
if (ownerRepo) {
@@ -126,6 +131,17 @@ export async function listIssues(
connectionId,
localGitOptions
)
// Why: a connection-backed request has no local cwd, so the non-GitHub
// fallback below would let gh list its default repository. Refuse instead.
if (connectionId && !ownerRepo) {
return {
items: [],
error: {
type: 'not_found',
message: 'Could not resolve GitHub owner/repo for this repository'
}
}
}
await acquire()
try {
if (ownerRepo) {
@@ -297,6 +297,8 @@ function normalizeItemDialogTab(
export type GitHubItemDialogProjectOrigin = {
owner: string
repo: string
/** GitHub host (e.g. GHES); absent means github.com. */
host?: string
number: number
type: 'issue' | 'pr'
projectId: string
@@ -399,6 +399,7 @@ export default function ProjectViewWrapper({ selectedRepoIds }: Props): React.JS
const [repoNotInOrca, setRepoNotInOrca] = useState<{
owner: string
repo: string
host?: string
url: string | null
} | null>(null)
const liveRepoIds = useMemo(() => new Set(repos.map((repo) => repo.id)), [repos])
@@ -597,6 +598,7 @@ export default function ProjectViewWrapper({ selectedRepoIds }: Props): React.JS
const resolution = resolveSelectedProjectRowRepo({
row,
lookupSlug,
host: table.project.host,
slugIndexReady,
selectedRepoIds
})
@@ -614,6 +616,7 @@ export default function ProjectViewWrapper({ selectedRepoIds }: Props): React.JS
setRepoNotInOrca({
owner: origin.owner,
repo: origin.repo,
host: origin.host,
url: row.content.url ?? null
})
return
@@ -6,27 +6,30 @@ type SlugProjectDialogState = {
origin: {
owner: string
repo: string
host?: string
}
}
type RepoNotInOrcaDialogState = {
owner: string
repo: string
host?: string
}
type RepoMatch = {
id: string
}
type LookupSlug = (slug: string) => readonly RepoMatch[]
type LookupSlug = (slug: string, host?: string) => readonly RepoMatch[]
function shouldCloseFallbackDialog(args: {
lookupSlug: LookupSlug
selectedRepoIds: ReadonlySet<string>
owner: string
repo: string
host?: string
}): boolean {
const matches = args.lookupSlug(`${args.owner}/${args.repo}`)
const matches = args.lookupSlug(`${args.owner}/${args.repo}`, args.host)
const selectedMatchCount = matches.filter((match) => args.selectedRepoIds.has(match.id)).length
const unselectedMatchCount = matches.length - selectedMatchCount
return selectedMatchCount > 0 || unselectedMatchCount > 0
@@ -67,7 +70,8 @@ export function resolveMissingRepoProjectDialogState<
lookupSlug,
selectedRepoIds,
owner: slugDialog.origin.owner,
repo: slugDialog.origin.repo
repo: slugDialog.origin.repo,
host: slugDialog.origin.host
})
? null
: slugDialog,
@@ -77,7 +81,8 @@ export function resolveMissingRepoProjectDialogState<
lookupSlug,
selectedRepoIds,
owner: repoNotInOrca.owner,
repo: repoNotInOrca.repo
repo: repoNotInOrca.repo,
host: repoNotInOrca.host
})
? null
: repoNotInOrca
+16 -10
View File
@@ -18,6 +18,7 @@ import {
getFreshMetadata,
loadMetadata
} from './metadata-request-cache'
import { githubRepoIdentityKey } from '../../../shared/github-repository-identity-key'
type MetadataState<T> = {
data: T
@@ -36,7 +37,8 @@ export function clearGitHubSlugMetadataCache(): void {
export function useRepoLabelsBySlug(
owner: string | null,
repo: string | null,
settings?: Pick<GlobalSettings, 'activeRuntimeEnvironmentId'> | null
settings?: Pick<GlobalSettings, 'activeRuntimeEnvironmentId'> | null,
host?: string
): MetadataState<string[]> {
const [state, setState] = useState<MetadataState<string[]>>({
data: [],
@@ -55,10 +57,11 @@ export function useRepoLabelsBySlug(
return
}
const target = getActiveRuntimeTarget({ activeRuntimeEnvironmentId })
const repositoryKey = githubRepoIdentityKey({ owner, repo, host })
const key =
target.kind === 'environment'
? `runtime:${target.environmentId}:${owner}/${repo}`
: `${owner}/${repo}`
? `runtime:${target.environmentId}:${repositoryKey}`
: repositoryKey
const cached = getFreshMetadata(slugLabelStore, key)
if (cached) {
@@ -85,10 +88,10 @@ export function useRepoLabelsBySlug(
? callRuntimeRpc<ListLabelsBySlugResult>(
target,
'github.project.listLabelsBySlug',
{ owner, repo },
{ owner, repo, ...(host ? { host } : {}) },
{ timeoutMs: 30_000 }
)
: window.api.gh.listLabelsBySlug({ owner, repo })
: window.api.gh.listLabelsBySlug({ owner, repo, ...(host ? { host } : {}) })
).then((res) => {
if (!res.ok) {
throw new Error(res.error.message)
@@ -113,7 +116,7 @@ export function useRepoLabelsBySlug(
error: err instanceof Error ? err.message : 'Failed to load labels'
}))
})
}, [owner, repo, activeRuntimeEnvironmentId])
}, [owner, repo, host, activeRuntimeEnvironmentId])
return state
}
@@ -122,7 +125,8 @@ export function useRepoAssigneesBySlug(
owner: string | null,
repo: string | null,
seedLogins?: string[],
settings?: Pick<GlobalSettings, 'activeRuntimeEnvironmentId'> | null
settings?: Pick<GlobalSettings, 'activeRuntimeEnvironmentId'> | null,
host?: string
): MetadataState<GitHubAssignableUser[]> {
const [state, setState] = useState<MetadataState<GitHubAssignableUser[]>>({
data: [],
@@ -143,10 +147,11 @@ export function useRepoAssigneesBySlug(
return
}
const target = getActiveRuntimeTarget({ activeRuntimeEnvironmentId })
const repositoryKey = githubRepoIdentityKey({ owner, repo, host })
const key =
target.kind === 'environment'
? `runtime:${target.environmentId}:${owner}/${repo}#${seedKey}`
: `${owner}/${repo}#${seedKey}`
? `runtime:${target.environmentId}:${repositoryKey}#${seedKey}`
: `${repositoryKey}#${seedKey}`
const cached = getFreshMetadata(slugAssigneeStore, key)
if (cached) {
@@ -173,6 +178,7 @@ export function useRepoAssigneesBySlug(
const args = {
owner,
repo,
...(host ? { host } : {}),
...(seedKey ? { seedLogins: seedKey.split(',') } : {})
}
loadMetadata(slugAssigneeStore, key, () =>
@@ -208,7 +214,7 @@ export function useRepoAssigneesBySlug(
error: err instanceof Error ? err.message : 'Failed to load assignees'
}))
})
}, [owner, repo, seedKey, activeRuntimeEnvironmentId])
}, [owner, repo, host, seedKey, activeRuntimeEnvironmentId])
return state
}
@@ -15,11 +15,17 @@ export function settingsForProjectRowOwner(
state: RepoOwnerState,
owner: string,
repo: string,
host?: string,
fallbackSettings:
| Pick<GlobalSettings, 'activeRuntimeEnvironmentId'>
| null
| undefined = state.settings
): Pick<GlobalSettings, 'activeRuntimeEnvironmentId'> | null | undefined {
const matchedRepo = lookupReposBySlugFromCache(state.repos, state.settings, `${owner}/${repo}`)[0]
const matchedRepo = lookupReposBySlugFromCache(
state.repos,
state.settings,
`${owner}/${repo}`,
host
)[0]
return matchedRepo ? getSettingsForRepoRuntimeOwner(state, matchedRepo.id) : fallbackSettings
}
+2
View File
@@ -2377,6 +2377,7 @@ export const createGitHubSlice: StateCreator<AppState, [], [], GitHubSlice> = (s
get(),
owner,
repo,
table.project.host,
settingsForProjectViewCacheKey(get().settings, cacheKey)
)
)
@@ -2506,6 +2507,7 @@ export const createGitHubSlice: StateCreator<AppState, [], [], GitHubSlice> = (s
get(),
owner,
repo,
table.project.host,
settingsForProjectViewCacheKey(get().settings, cacheKey)
)
)