mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 08:37:49 +00:00
* feat(github): bind projects to a specific gh account Adds per-project `Repo.ghAccount` so repo-scoped gh calls (create-worktree issue/PR search, work items, hosted-review reads and mutations) run as the bound account via ephemeral child-env token injection instead of the globally active gh login. Multi-account resolution is capability-gated (gh >= 2.40) and fails closed when the bound account or host is unavailable; Project View stays ambient by design. Repository settings gains a section for selecting or clearing a keyring-backed account (shadcn `Select`), with mixed-version "not enforced" handling for older remote runtimes. Attached `-Rhost/owner/repo` forms are covered by the host-drift guard and its tests; es/ja/ko/zh catalogs carry the section's strings. `getLocalProjectGhExecOptions` centralizes the binding lookup so every gh execution path picks it up, including the Electron `hostedReview:*` handlers that previously stayed on the ambient login. `gh auth token` (a keyring read) is exempt from the rate-limit breaker gate so a tripped bucket cannot turn a bound-token resolve into a false "unavailable". The `ghAccount` update field and the two binding RPC methods live in the shared RPC params contract; the generated catalog is regenerated. Fixes #13612 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012B3QEP5iP4WGGEpPLtkHqA * fix(settings): make GitHub account refresh secondary * fix(github): satisfy strict casting quality checks * test(rpc): use runtime fixture for repo binding * fix(github): preserve project account for PR worktree lookups * test(rpc): avoid incomplete runtime settings fixture * fix(i18n): add GitHub account refresh label * fix(i18n): refresh runtime required catalog * fix(windows): preserve mobile patch bytes --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Neil <neil@stably.ai>
54 lines
2.0 KiB
TypeScript
54 lines
2.0 KiB
TypeScript
import type { IssueSourcePreference } from '../../shared/repo-types'
|
|
import type { GhAccountBinding } from '../../shared/github/account-binding'
|
|
import { pickPreferredGitRemote } from '../../shared/preferred-git-remote'
|
|
import { getDefaultRemote } from '../git/repo'
|
|
import { getGitHubApiRepositoryForRemote } from './github-api-repository'
|
|
|
|
type GitExec = (args: string[]) => Promise<{ stdout: string; stderr: string }>
|
|
|
|
// Why: explicit origin must match issue listing; otherwise hosting identity
|
|
// keeps contributor clones on the upstream project's PR namespace.
|
|
export async function resolveGitHubReviewHeadRemote(args: {
|
|
repoPath: string
|
|
issueSourcePreference?: IssueSourcePreference
|
|
connectionId?: string | null
|
|
localGitOptions?: { wslDistro?: string; ghAccount?: GhAccountBinding }
|
|
gitExec: GitExec
|
|
}): Promise<string> {
|
|
const { stdout } = await args.gitExec(['remote'])
|
|
const remotes = stdout
|
|
.split(/\r?\n/)
|
|
.map((line) => line.trim())
|
|
.filter(Boolean)
|
|
if (args.issueSourcePreference === 'origin') {
|
|
if (remotes.includes('origin')) {
|
|
return 'origin'
|
|
}
|
|
throw new Error('Repo has no configured origin remote.')
|
|
}
|
|
// Why: identity probes cost a `remote get-url` (plus a possible gh auth
|
|
// lookup) each; only multi-remote clones are ambiguous enough to need them.
|
|
if (remotes.length > 1) {
|
|
for (const remote of ['upstream', 'origin']) {
|
|
if (!remotes.includes(remote)) {
|
|
continue
|
|
}
|
|
const repository = await getGitHubApiRepositoryForRemote(
|
|
args.repoPath,
|
|
remote,
|
|
args.connectionId ?? null,
|
|
args.localGitOptions ?? {}
|
|
)
|
|
if (repository) {
|
|
return remote
|
|
}
|
|
}
|
|
}
|
|
// Why: when no remote maps to a GitHub project the hosting identity cannot
|
|
// guide the choice; keep the legacy per-transport fallback.
|
|
if (args.connectionId) {
|
|
return pickPreferredGitRemote(remotes)
|
|
}
|
|
return getDefaultRemote(args.repoPath, args.localGitOptions ?? {})
|
|
}
|