mirror of
https://github.com/stablyai/orca.git
synced 2026-10-08 08:02:32 +00:00
fix(github): align PR source and review head origin (#10677)
* fix(github): align PR source and review head origin * fix(github): pin number-based work item open to the repo source preference Open-by-number and details still ran the upstream-first multi-candidate PR probe, so a fork and its upstream sharing a PR number opened different PRs than the list and start-point paths did once #10677 pinned those to origin. Thread repo.issueSourcePreference through dispatchWorkItem, getWorkItemDetails, getRepoWorkItem, and getRepoWorkItemDetails. getWorkItemByOwnerRepo is left alone: explicit owner/repo already pins identity. auto/upstream/undefined keep the multi-candidate probe. Co-authored-by: Orca <help@stably.ai> * test(github): enforce origin preference in review head origin resolution The explicit origin preference must short-circuit before any identity probe, so no remote queries should occur. Add validation to reject unexpected remotes and tighten the test assertion to verify no remote get-url calls happen at all. * fix(github): enforce origin preference in issue open-by-number lookup listWorkItems and getWorkItem must share preference so origin/upstream toggles cannot disagree. Explicit origin preference now fail-closes when origin identity is unresolved (no bare-lookup fallback), matching the PR candidate resolution rule. --------- Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
+57
-16
@@ -76,7 +76,6 @@ import { shouldHideNonOpenReviewOnDefaultBranch } from '../source-control/repo-d
|
||||
import { readLocalGitConfigSignature } from './local-git-config-signature'
|
||||
import {
|
||||
getGitHubApiRepositoryForRemote,
|
||||
getIssueGitHubApiRepository,
|
||||
getOriginGitHubApiRepository,
|
||||
githubHostExecOptions,
|
||||
githubRepositorySlugArg,
|
||||
@@ -283,16 +282,35 @@ export type PullRequestPushTarget = {
|
||||
maintainerCanModify?: boolean
|
||||
}
|
||||
|
||||
// Why: only an explicit `origin` preference is origin-only; `upstream`/`auto`/
|
||||
// undefined keep the multi-candidate probe ordered upstream-first, matching
|
||||
// resolvePrWorkItemSource list semantics.
|
||||
async function resolvePullRequestLookupCandidates(
|
||||
repoPath: string,
|
||||
preference: IssueSourcePreference | undefined,
|
||||
connectionId?: string | null,
|
||||
localGitOptions: LocalGitExecOptions = {}
|
||||
): Promise<GitHubApiRepository[]> {
|
||||
if (preference === 'origin') {
|
||||
const origin = await getOriginGitHubApiRepository(repoPath, connectionId, localGitOptions)
|
||||
return origin ? [origin] : []
|
||||
}
|
||||
return (await resolveGitHubApiRepositoryCandidates(repoPath, connectionId, localGitOptions))
|
||||
.candidates
|
||||
}
|
||||
|
||||
export async function getPullRequestPushTarget(
|
||||
repoPath: string,
|
||||
prNumber: number,
|
||||
connectionId?: string | null,
|
||||
localGitOptions: LocalGitExecOptions = {}
|
||||
localGitOptions: LocalGitExecOptions = {},
|
||||
preference?: IssueSourcePreference
|
||||
): Promise<PullRequestPushTarget | null> {
|
||||
const context = githubRepoContext(repoPath, connectionId, localGitOptions)
|
||||
const ghOptions = ghRepoExecOptions(context)
|
||||
const { candidates } = await resolveGitHubApiRepositoryCandidates(
|
||||
const candidates = await resolvePullRequestLookupCandidates(
|
||||
repoPath,
|
||||
preference,
|
||||
connectionId,
|
||||
localGitOptions
|
||||
)
|
||||
@@ -954,14 +972,19 @@ async function fetchPullRequestWorkItemFromCandidates(
|
||||
repoPath: string,
|
||||
number: number,
|
||||
connectionId?: string | null,
|
||||
localGitOptions: LocalGitExecOptions = {}
|
||||
localGitOptions: LocalGitExecOptions = {},
|
||||
preference?: IssueSourcePreference
|
||||
): Promise<MainWorkItem | null> {
|
||||
const { candidates } = await resolveGitHubApiRepositoryCandidates(
|
||||
const candidates = await resolvePullRequestLookupCandidates(
|
||||
repoPath,
|
||||
preference,
|
||||
connectionId,
|
||||
localGitOptions
|
||||
)
|
||||
if (candidates.length === 0) {
|
||||
if (preference === 'origin') {
|
||||
return null
|
||||
}
|
||||
return fetchPullRequestWorkItem(repoPath, null, number, connectionId, localGitOptions)
|
||||
}
|
||||
for (const candidate of candidates) {
|
||||
@@ -1953,38 +1976,55 @@ export async function getWorkItem(
|
||||
number: number,
|
||||
type?: 'issue' | 'pr',
|
||||
connectionId?: string | null,
|
||||
localGitOptions: LocalGitExecOptions = {}
|
||||
localGitOptions: LocalGitExecOptions = {},
|
||||
preference?: IssueSourcePreference
|
||||
): Promise<MainWorkItem | null> {
|
||||
await acquire()
|
||||
try {
|
||||
// Why: listWorkItems uses resolveIssueGitHubApiRepositorySource; open-by-number
|
||||
// must share that preference so origin/upstream toggles cannot disagree.
|
||||
if (type === 'issue') {
|
||||
return await fetchIssueWorkItem(
|
||||
const { source } = await resolveIssueGitHubApiRepositorySource(
|
||||
repoPath,
|
||||
await getIssueGitHubApiRepository(repoPath, connectionId, localGitOptions),
|
||||
number,
|
||||
preference,
|
||||
connectionId,
|
||||
localGitOptions
|
||||
)
|
||||
// Why: explicit origin with no origin identity must not bare-lookup ambient gh
|
||||
// (same fail-closed rule as origin-pinned PR candidate resolution).
|
||||
if (!source && preference === 'origin') {
|
||||
return null
|
||||
}
|
||||
return await fetchIssueWorkItem(repoPath, source, number, connectionId, localGitOptions)
|
||||
}
|
||||
if (type === 'pr') {
|
||||
return await fetchPullRequestWorkItemFromCandidates(
|
||||
repoPath,
|
||||
number,
|
||||
connectionId,
|
||||
localGitOptions
|
||||
localGitOptions,
|
||||
preference
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
const issue = await fetchIssueWorkItem(
|
||||
const { source } = await resolveIssueGitHubApiRepositorySource(
|
||||
repoPath,
|
||||
await getIssueGitHubApiRepository(repoPath, connectionId, localGitOptions),
|
||||
number,
|
||||
preference,
|
||||
connectionId,
|
||||
localGitOptions
|
||||
)
|
||||
if (issue) {
|
||||
return issue
|
||||
if (source || preference !== 'origin') {
|
||||
const issue = await fetchIssueWorkItem(
|
||||
repoPath,
|
||||
source,
|
||||
number,
|
||||
connectionId,
|
||||
localGitOptions
|
||||
)
|
||||
if (issue) {
|
||||
return issue
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// Why: only fall through to PR #N on a genuine 404; re-throw transient errors so a flake can't surface an unrelated PR.
|
||||
@@ -1997,7 +2037,8 @@ export async function getWorkItem(
|
||||
repoPath,
|
||||
number,
|
||||
connectionId,
|
||||
localGitOptions
|
||||
localGitOptions,
|
||||
preference
|
||||
)
|
||||
} catch {
|
||||
return null
|
||||
|
||||
Reference in New Issue
Block a user