mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
Accept merged fallback PRs during branch lookup (#5908)
Ensure that when a visible fallback PR has been merged (e.g., outside Orca with a deleted head branch), it is still accepted and refreshed by branch lookup instead of being discarded as an implicit merged PR. * Add `acceptMergedFallbackPR` option to GitHub branch lookups * Enable this option during manual and background refreshes of fallback PRs * Plumb the new option through preload APIs, IPC handlers, and RPC protocols
This commit is contained in:
@@ -823,6 +823,76 @@ describe('getPRForBranch', () => {
|
||||
expect(pr).toMatchObject({ number: 42, title: 'Open fallback PR' })
|
||||
})
|
||||
|
||||
it('returns a merged PR when branch lookup and fallback point at the same PR', async () => {
|
||||
getOwnerRepoMock.mockResolvedValueOnce({ owner: 'acme', repo: 'widgets' })
|
||||
ghExecFileAsyncMock
|
||||
.mockResolvedValueOnce({
|
||||
stdout: JSON.stringify([
|
||||
{
|
||||
number: 5511,
|
||||
title: 'Merged current PR',
|
||||
state: 'closed',
|
||||
merged_at: '2026-06-16T17:15:33Z',
|
||||
html_url: 'https://github.com/acme/widgets/pull/5511',
|
||||
updated_at: '2026-06-16T17:15:33Z',
|
||||
draft: false,
|
||||
mergeable_state: 'clean',
|
||||
head: { ref: 'feature/test', sha: 'merged-head-oid' },
|
||||
base: { ref: 'main', sha: 'base-oid' }
|
||||
}
|
||||
])
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
stdout: JSON.stringify({
|
||||
number: 5511,
|
||||
title: 'Merged current PR',
|
||||
state: 'MERGED',
|
||||
url: 'https://github.com/acme/widgets/pull/5511',
|
||||
statusCheckRollup: [],
|
||||
updatedAt: '2026-06-16T17:15:33Z',
|
||||
isDraft: false,
|
||||
mergeable: 'MERGEABLE',
|
||||
baseRefName: 'main',
|
||||
headRefName: 'feature/test',
|
||||
baseRefOid: 'base-oid',
|
||||
headRefOid: 'merged-head-oid'
|
||||
})
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
stdout: JSON.stringify({
|
||||
number: 5511,
|
||||
title: 'Merged current PR',
|
||||
state: 'MERGED',
|
||||
url: 'https://github.com/acme/widgets/pull/5511',
|
||||
statusCheckRollup: [],
|
||||
updatedAt: '2026-06-16T17:15:33Z',
|
||||
isDraft: false,
|
||||
mergeable: 'MERGEABLE',
|
||||
baseRefName: 'main',
|
||||
headRefName: 'feature/test',
|
||||
baseRefOid: 'base-oid',
|
||||
headRefOid: 'merged-head-oid'
|
||||
})
|
||||
})
|
||||
|
||||
const pr = await getPRForBranch('/repo-root', 'feature/test', null, null, 5511)
|
||||
|
||||
expect(ghExecFileAsyncMock).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
[
|
||||
'pr',
|
||||
'view',
|
||||
'5511',
|
||||
'--repo',
|
||||
'acme/widgets',
|
||||
'--json',
|
||||
'number,title,state,url,statusCheckRollup,updatedAt,isDraft,mergeable,reviewDecision,mergeStateStatus,autoMergeRequest,baseRefName,headRefName,baseRefOid,headRefOid'
|
||||
],
|
||||
{ cwd: '/repo-root' }
|
||||
)
|
||||
expect(pr).toMatchObject({ number: 5511, state: 'merged', title: 'Merged current PR' })
|
||||
})
|
||||
|
||||
it('does not carry a merged upstream branch head repo into a fallback PR number', async () => {
|
||||
resolvePRRepositoryCandidatesMock.mockResolvedValueOnce({
|
||||
candidates: [{ owner: 'stablyai', repo: 'orca' }],
|
||||
@@ -925,6 +995,38 @@ describe('getPRForBranch', () => {
|
||||
expect(pr).toBeNull()
|
||||
})
|
||||
|
||||
it('returns a merged fallback PR when visible fallback lifecycle is accepted', async () => {
|
||||
getOwnerRepoMock.mockResolvedValueOnce({ owner: 'acme', repo: 'widgets' })
|
||||
ghExecFileAsyncMock
|
||||
.mockResolvedValueOnce({ stdout: JSON.stringify([]) })
|
||||
.mockResolvedValueOnce({
|
||||
stdout: JSON.stringify({
|
||||
number: 5511,
|
||||
title: 'Merged visible fallback PR',
|
||||
state: 'MERGED',
|
||||
url: 'https://github.com/acme/widgets/pull/5511',
|
||||
statusCheckRollup: [],
|
||||
updatedAt: '2026-06-16T17:15:33Z',
|
||||
isDraft: false,
|
||||
mergeable: 'MERGEABLE',
|
||||
baseRefName: 'main',
|
||||
headRefName: 'deleted-head',
|
||||
baseRefOid: 'base-oid',
|
||||
headRefOid: 'head-oid'
|
||||
})
|
||||
})
|
||||
|
||||
const pr = await getPRForBranch('/repo-root', 'deleted-head', null, null, 5511, {
|
||||
acceptMergedFallbackPR: true
|
||||
})
|
||||
|
||||
expect(pr).toMatchObject({
|
||||
number: 5511,
|
||||
state: 'merged',
|
||||
title: 'Merged visible fallback PR'
|
||||
})
|
||||
})
|
||||
|
||||
it('falls back to the tracked upstream branch when the local branch name differs', async () => {
|
||||
getOwnerRepoMock.mockResolvedValueOnce({ owner: 'acme', repo: 'widgets' })
|
||||
ghExecFileAsyncMock
|
||||
|
||||
@@ -1989,6 +1989,10 @@ const PR_LOOKUP_JSON_FIELDS =
|
||||
const PR_BRANCH_LIST_JSON_FIELDS =
|
||||
'number,title,state,url,statusCheckRollup,updatedAt,isDraft,mergeable,baseRefName,headRefName,baseRefOid,headRefOid'
|
||||
|
||||
export type GitHubPRBranchLookupOptions = HostedReviewExecutionOptions & {
|
||||
acceptMergedFallbackPR?: boolean
|
||||
}
|
||||
|
||||
function mapRestPRMergeable(pr: RestPullRequest): PRMergeableState {
|
||||
const mergeableState = pr.mergeable_state?.toLowerCase()
|
||||
if (mergeableState === 'dirty') {
|
||||
@@ -2455,7 +2459,7 @@ export async function getPRForBranch(
|
||||
linkedPRNumber?: number | null,
|
||||
connectionId?: string | null,
|
||||
fallbackPRNumber?: number | null,
|
||||
options: HostedReviewExecutionOptions = {}
|
||||
options: GitHubPRBranchLookupOptions = {}
|
||||
): Promise<PRInfo | null> {
|
||||
const outcome = await getPRForBranchOutcome(
|
||||
repoPath,
|
||||
@@ -2474,7 +2478,7 @@ export async function getPRForBranchOutcome(
|
||||
linkedPRNumber?: number | null,
|
||||
connectionId?: string | null,
|
||||
fallbackPRNumber?: number | null,
|
||||
options: HostedReviewExecutionOptions = {}
|
||||
options: GitHubPRBranchLookupOptions = {}
|
||||
): Promise<PRRefreshOutcome> {
|
||||
// Strip refs/heads/ prefix if present
|
||||
const branchName = branch.replace(/^refs\/heads\//, '')
|
||||
@@ -2549,7 +2553,9 @@ export async function getPRForBranchOutcome(
|
||||
}
|
||||
}
|
||||
}
|
||||
let mergedBranchLookupNumber: number | null = null
|
||||
if (data && isMergedImplicitPR(data, linkedPRNumber)) {
|
||||
mergedBranchLookupNumber = data.number
|
||||
data = null
|
||||
dataRepo = null
|
||||
dataHeadRepo = headRepo
|
||||
@@ -2566,7 +2572,18 @@ export async function getPRForBranchOutcome(
|
||||
if (!data) {
|
||||
return { kind: 'no-pr', fetchedAt: Date.now() }
|
||||
}
|
||||
if (isMergedImplicitPR(data, linkedPRNumber)) {
|
||||
const fallbackConfirmedMergedBranch =
|
||||
typeof fallbackPRNumber === 'number' &&
|
||||
mergedBranchLookupNumber === fallbackPRNumber &&
|
||||
data.number === fallbackPRNumber
|
||||
// Why: a currently visible PR can be merged outside Orca; when the caller
|
||||
// marks the fallback as visible review state, keep its lifecycle fresh even
|
||||
// if GitHub no longer reports it by branch (for example deleted heads).
|
||||
if (
|
||||
isMergedImplicitPR(data, linkedPRNumber) &&
|
||||
!fallbackConfirmedMergedBranch &&
|
||||
options.acceptMergedFallbackPR !== true
|
||||
) {
|
||||
return { kind: 'no-pr', fetchedAt: Date.now() }
|
||||
}
|
||||
|
||||
|
||||
@@ -309,6 +309,31 @@ describe('pr-refresh-coordinator', () => {
|
||||
expect(outcome?.sequence).toBe(inFlight?.sequence)
|
||||
})
|
||||
|
||||
it('accepts merged fallback PRs for visible fallback refreshes', async () => {
|
||||
const { refreshPRNow } = await import('./pr-refresh-coordinator')
|
||||
getPRForBranchOutcomeMock.mockResolvedValueOnce({
|
||||
kind: 'found',
|
||||
pr: makePR({ state: 'merged' }),
|
||||
fetchedAt: Date.now()
|
||||
})
|
||||
|
||||
await refreshPRNow(
|
||||
makeCandidate({
|
||||
fallbackPRNumber: 12,
|
||||
fallbackPRSource: 'pr-cache'
|
||||
})
|
||||
)
|
||||
|
||||
expect(getPRForBranchOutcomeMock).toHaveBeenCalledWith(
|
||||
'/repo',
|
||||
'feature/test',
|
||||
null,
|
||||
null,
|
||||
12,
|
||||
{ acceptMergedFallbackPR: true }
|
||||
)
|
||||
})
|
||||
|
||||
it('does not coalesce local and SSH refreshes for the same branch', async () => {
|
||||
const { enqueuePRRefresh } = await import('./pr-refresh-coordinator')
|
||||
getPRForBranchOutcomeMock
|
||||
|
||||
@@ -10,8 +10,7 @@ import type {
|
||||
GitHubPRRefreshSkippedReason,
|
||||
PRRefreshOutcome
|
||||
} from '../../shared/types'
|
||||
import type { HostedReviewExecutionOptions } from '../source-control/hosted-review-git-options'
|
||||
import { getPRForBranchOutcome } from './client'
|
||||
import { getPRForBranchOutcome, type GitHubPRBranchLookupOptions } from './client'
|
||||
import { getRateLimit, noteRateLimitSpend, rateLimitGuard } from './rate-limit'
|
||||
|
||||
type QueueEntry = {
|
||||
@@ -30,12 +29,30 @@ type PRRefreshOutcomeObserver = (
|
||||
outcome: PRRefreshOutcome
|
||||
) => void
|
||||
|
||||
type PRBranchLookupCandidate = Pick<
|
||||
GitHubPRRefreshCandidate,
|
||||
'localGitOptions' | 'linkedPRNumber' | 'fallbackPRNumber' | 'fallbackPRSource'
|
||||
>
|
||||
|
||||
function shouldAcceptMergedFallbackPR(candidate: PRBranchLookupCandidate): boolean {
|
||||
return (
|
||||
candidate.linkedPRNumber == null &&
|
||||
candidate.fallbackPRNumber != null &&
|
||||
candidate.fallbackPRSource != null
|
||||
)
|
||||
}
|
||||
|
||||
function hostedReviewOptionArgs(
|
||||
localGitOptions?: GitHubPRRefreshCandidate['localGitOptions']
|
||||
): [] | [HostedReviewExecutionOptions] {
|
||||
return localGitOptions?.wslDistro
|
||||
? [{ localGitExecOptions: { wslDistro: localGitOptions.wslDistro } }]
|
||||
: []
|
||||
candidate: PRBranchLookupCandidate
|
||||
): [] | [GitHubPRBranchLookupOptions] {
|
||||
const options: GitHubPRBranchLookupOptions = {}
|
||||
if (candidate.localGitOptions?.wslDistro) {
|
||||
options.localGitExecOptions = { wslDistro: candidate.localGitOptions.wslDistro }
|
||||
}
|
||||
if (shouldAcceptMergedFallbackPR(candidate)) {
|
||||
options.acceptMergedFallbackPR = true
|
||||
}
|
||||
return Object.keys(options).length > 0 ? [options] : []
|
||||
}
|
||||
|
||||
const MIN_BACKGROUND_REFRESH_AGE_MS = 60_000
|
||||
@@ -532,7 +549,7 @@ async function drainQueue(): Promise<void> {
|
||||
next.candidate.linkedPRNumber ?? null,
|
||||
next.candidate.connectionId ?? null,
|
||||
next.candidate.linkedPRNumber == null ? (next.candidate.fallbackPRNumber ?? null) : null,
|
||||
...hostedReviewOptionArgs(next.candidate.localGitOptions)
|
||||
...hostedReviewOptionArgs(next.candidate)
|
||||
)
|
||||
outcomeObserver?.(next.candidate, outcome)
|
||||
broadcast({ aliases, reason: next.reason, outcome, requestStartedAt }, requestSequence)
|
||||
@@ -659,7 +676,7 @@ export async function refreshPRNow(candidate: GitHubPRRefreshCandidate): Promise
|
||||
candidate.linkedPRNumber ?? null,
|
||||
candidate.connectionId ?? null,
|
||||
candidate.linkedPRNumber == null ? (candidate.fallbackPRNumber ?? null) : null,
|
||||
...hostedReviewOptionArgs(candidate.localGitOptions)
|
||||
...hostedReviewOptionArgs(candidate)
|
||||
)
|
||||
outcomeObserver?.(candidate, outcome)
|
||||
broadcast({ aliases, reason: 'manual', outcome, requestStartedAt }, requestSequence)
|
||||
|
||||
@@ -381,6 +381,26 @@ describe('gitlab client — MR operations', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('preserves merged state when falling back to a linked MR iid', async () => {
|
||||
getProjectRefMock.mockResolvedValueOnce({ host: 'gitlab.com', path: 'g/p' })
|
||||
glabExecFileAsyncMock.mockResolvedValueOnce({ stdout: '[]' }).mockResolvedValueOnce({
|
||||
stdout: JSON.stringify({
|
||||
iid: 10,
|
||||
title: 'Merged linked MR',
|
||||
state: 'merged',
|
||||
pipeline: { status: 'success' }
|
||||
})
|
||||
})
|
||||
|
||||
const mr = await getMergeRequestForBranch('/repo', 'local-review-branch', 10)
|
||||
|
||||
expect(mr).toMatchObject({
|
||||
number: 10,
|
||||
state: 'merged',
|
||||
pipelineStatus: 'success'
|
||||
})
|
||||
})
|
||||
|
||||
it('routes local WSL merge-request branch lookup through the selected distro', async () => {
|
||||
getProjectRefMock.mockResolvedValueOnce({ host: 'gitlab.com', path: 'g/p' })
|
||||
glabExecFileAsyncMock.mockResolvedValueOnce({
|
||||
|
||||
+14
-1
@@ -51,6 +51,7 @@ import {
|
||||
checkOrcaStarred,
|
||||
starOrca
|
||||
} from '../github/client'
|
||||
import type { GitHubPRBranchLookupOptions } from '../github/client'
|
||||
import {
|
||||
clearVisiblePRRefreshWindow,
|
||||
enqueuePRRefresh,
|
||||
@@ -201,19 +202,31 @@ export function registerGitHubHandlers(store: Store, stats: StatsCollector): voi
|
||||
branch: string
|
||||
linkedPRNumber?: number | null
|
||||
fallbackPRNumber?: number | null
|
||||
acceptMergedFallbackPR?: boolean
|
||||
}
|
||||
) => {
|
||||
const repo = assertRegisteredRepo(args, store)
|
||||
const localGitOptions = localGitOptionArgs(store, repo)[0]
|
||||
const hostedReviewOptionArgs: [] | [{ localGitExecOptions: { wslDistro?: string } }] =
|
||||
localGitOptions ? [{ localGitExecOptions: localGitOptions }] : []
|
||||
const lookupOptions: GitHubPRBranchLookupOptions | undefined = hostedReviewOptionArgs[0]
|
||||
? { ...hostedReviewOptionArgs[0] }
|
||||
: args.acceptMergedFallbackPR === true
|
||||
? {}
|
||||
: undefined
|
||||
if (lookupOptions && args.acceptMergedFallbackPR === true) {
|
||||
lookupOptions.acceptMergedFallbackPR = true
|
||||
}
|
||||
const lookupOptionArgs: [] | [GitHubPRBranchLookupOptions] = lookupOptions
|
||||
? [lookupOptions]
|
||||
: []
|
||||
const pr = await getPRForBranch(
|
||||
repo.path,
|
||||
args.branch,
|
||||
args.linkedPRNumber ?? null,
|
||||
repoConnectionId(repo),
|
||||
args.linkedPRNumber == null ? (args.fallbackPRNumber ?? null) : null,
|
||||
...hostedReviewOptionArgs
|
||||
...lookupOptionArgs
|
||||
)
|
||||
// Emit pr_created when a PR is first detected for a branch.
|
||||
// Why here: the renderer polls gh:prForBranch to check PR status per worktree.
|
||||
|
||||
@@ -333,6 +333,7 @@ import {
|
||||
listLabels,
|
||||
listAssignableUsers
|
||||
} from '../github/client'
|
||||
import type { GitHubPRBranchLookupOptions } from '../github/client'
|
||||
import { resolveGitHubPrStartPoint } from '../github/pr-start-point'
|
||||
import { fetchPrHeadTrackingRef } from '../github/pr-head-tracking-ref'
|
||||
import { getWorkItemDetails, getPRFileContents } from '../github/work-item-details'
|
||||
@@ -9964,17 +9965,24 @@ export class OrcaRuntimeService {
|
||||
repoSelector: string,
|
||||
branch: string,
|
||||
linkedPRNumber?: number | null,
|
||||
fallbackPRNumber?: number | null
|
||||
fallbackPRNumber?: number | null,
|
||||
acceptMergedFallbackPR?: boolean
|
||||
): Promise<Awaited<ReturnType<typeof getPRForBranch>>> {
|
||||
const repo = await this.resolveRepoSelector(repoSelector)
|
||||
const options = this.getHostedReviewExecutionOptions(repo)
|
||||
const options: GitHubPRBranchLookupOptions = this.getHostedReviewExecutionOptions(repo) ?? {}
|
||||
const lookupOptions = { ...options }
|
||||
if (acceptMergedFallbackPR === true) {
|
||||
lookupOptions.acceptMergedFallbackPR = true
|
||||
}
|
||||
const lookupOptionArgs: [] | [GitHubPRBranchLookupOptions] =
|
||||
Object.keys(lookupOptions).length > 0 ? [lookupOptions] : []
|
||||
return getPRForBranch(
|
||||
repo.path,
|
||||
branch,
|
||||
linkedPRNumber ?? null,
|
||||
repo.connectionId ?? null,
|
||||
linkedPRNumber == null ? (fallbackPRNumber ?? null) : null,
|
||||
options
|
||||
...lookupOptionArgs
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,8 @@ const SlugAssignableUsers = SlugRepo.extend({
|
||||
const PrForBranch = RepoSelector.extend({
|
||||
branch: requiredString('Missing branch'),
|
||||
linkedPRNumber: z.number().int().positive().nullable().optional(),
|
||||
fallbackPRNumber: z.number().int().positive().nullable().optional()
|
||||
fallbackPRNumber: z.number().int().positive().nullable().optional(),
|
||||
acceptMergedFallbackPR: z.boolean().optional()
|
||||
})
|
||||
|
||||
const Issue = RepoSelector.extend({
|
||||
@@ -359,7 +360,8 @@ export const GITHUB_METHODS: RpcMethod[] = [
|
||||
params.repo,
|
||||
params.branch,
|
||||
params.linkedPRNumber,
|
||||
params.fallbackPRNumber
|
||||
params.fallbackPRNumber,
|
||||
params.acceptMergedFallbackPR
|
||||
)
|
||||
}),
|
||||
defineMethod({
|
||||
|
||||
@@ -269,6 +269,8 @@ describe('forge provider interface', () => {
|
||||
number: 7,
|
||||
status: 'success'
|
||||
})
|
||||
expect(getPRForBranchMock).toHaveBeenCalledWith('/repo', '', null, 'ssh-1', 7)
|
||||
expect(getPRForBranchMock).toHaveBeenCalledWith('/repo', '', null, 'ssh-1', 7, {
|
||||
acceptMergedFallbackPR: true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -120,7 +120,10 @@ const gitHubForgeProvider = {
|
||||
input.linkedReviewNumber ?? null,
|
||||
input.connectionId,
|
||||
fallbackReviewNumber,
|
||||
...executionArgs
|
||||
{
|
||||
...executionArgs[0],
|
||||
acceptMergedFallbackPR: true
|
||||
}
|
||||
)
|
||||
: executionArgs.length > 0
|
||||
? await getPRForBranch(
|
||||
|
||||
@@ -194,7 +194,9 @@ describe('getHostedReviewForBranch', () => {
|
||||
number: 42,
|
||||
status: 'success'
|
||||
})
|
||||
expect(getPRForBranchMock).toHaveBeenCalledWith('/repo', '', null, undefined, 42)
|
||||
expect(getPRForBranchMock).toHaveBeenCalledWith('/repo', '', null, undefined, 42, {
|
||||
acceptMergedFallbackPR: true
|
||||
})
|
||||
})
|
||||
|
||||
it('falls through to Bitbucket when origin is not GitLab or GitHub', async () => {
|
||||
|
||||
@@ -1147,6 +1147,7 @@ export type PreloadApi = {
|
||||
branch: string
|
||||
linkedPRNumber?: number | null
|
||||
fallbackPRNumber?: number | null
|
||||
acceptMergedFallbackPR?: boolean
|
||||
}) => Promise<PRInfo | null>
|
||||
refreshPRNow: (args: { candidate: GitHubPRRefreshCandidate }) => Promise<PRRefreshOutcome>
|
||||
enqueuePRRefresh: (args: {
|
||||
|
||||
@@ -943,6 +943,7 @@ const api = {
|
||||
branch: string
|
||||
linkedPRNumber?: number | null
|
||||
fallbackPRNumber?: number | null
|
||||
acceptMergedFallbackPR?: boolean
|
||||
}): Promise<unknown> => ipcRenderer.invoke('gh:prForBranch', args),
|
||||
|
||||
refreshPRNow: (args: { candidate: GitHubPRRefreshCandidate }): Promise<unknown> =>
|
||||
|
||||
@@ -2659,7 +2659,9 @@ export const createGitHubSlice: StateCreator<AppState, [], [], GitHubSlice> = (s
|
||||
repo: runtimeRepo.repo.id,
|
||||
branch,
|
||||
linkedPRNumber,
|
||||
...(fallbackPRNumber !== null ? { fallbackPRNumber } : {})
|
||||
...(fallbackPRNumber !== null
|
||||
? { fallbackPRNumber, acceptMergedFallbackPR: fallbackPRSource !== null }
|
||||
: {})
|
||||
},
|
||||
{ timeoutMs: 30_000 }
|
||||
).then((pr) =>
|
||||
@@ -2690,7 +2692,14 @@ export const createGitHubSlice: StateCreator<AppState, [], [], GitHubSlice> = (s
|
||||
return window.api.gh.refreshPRNow
|
||||
? await window.api.gh.refreshPRNow({ candidate })
|
||||
: await window.api.gh
|
||||
.prForBranch({ repoPath, repoId, branch, linkedPRNumber, fallbackPRNumber })
|
||||
.prForBranch({
|
||||
repoPath,
|
||||
repoId,
|
||||
branch,
|
||||
linkedPRNumber,
|
||||
fallbackPRNumber,
|
||||
acceptMergedFallbackPR: fallbackPRNumber !== null && fallbackPRSource !== null
|
||||
})
|
||||
.then((pr) =>
|
||||
pr
|
||||
? ({ kind: 'found', pr, fetchedAt: Date.now() } as const)
|
||||
|
||||
@@ -2331,7 +2331,8 @@ describe('web GitHub preload API', () => {
|
||||
repo: 'id:repo-1',
|
||||
branch: 'feature',
|
||||
linkedPRNumber: null,
|
||||
fallbackPRNumber: 9
|
||||
fallbackPRNumber: 9,
|
||||
acceptMergedFallbackPR: true
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
@@ -1675,12 +1675,17 @@ function createGitHubApi(): WebGitHubApi {
|
||||
prForBranch: (args) =>
|
||||
route<WebGitHubResult<'prForBranch'>>(GITHUB_WEB_RPC_METHODS.prForBranch, args),
|
||||
refreshPRNow: async ({ candidate }) => {
|
||||
const acceptMergedFallbackPR =
|
||||
candidate.linkedPRNumber == null &&
|
||||
candidate.fallbackPRNumber != null &&
|
||||
candidate.fallbackPRSource != null
|
||||
const pr = await route<WebGitHubResult<'prForBranch'>>(GITHUB_WEB_RPC_METHODS.prForBranch, {
|
||||
repoPath: candidate.repoPath,
|
||||
repoId: candidate.repoId,
|
||||
branch: candidate.branch,
|
||||
linkedPRNumber: candidate.linkedPRNumber ?? null,
|
||||
fallbackPRNumber: candidate.fallbackPRNumber ?? null
|
||||
fallbackPRNumber: candidate.fallbackPRNumber ?? null,
|
||||
...(acceptMergedFallbackPR ? { acceptMergedFallbackPR: true } : {})
|
||||
})
|
||||
return pr
|
||||
? { kind: 'found', pr, fetchedAt: Date.now() }
|
||||
|
||||
Reference in New Issue
Block a user