fix(git): skip upstream remote probes when the remote is absent (#18455)

* fix(git): skip upstream remote probes when the remote is absent

Issue and PR resolvers listed remotes by probing `git remote get-url
upstream` on every poll, including origin-only clones where that remote
cannot exist. List remotes once, cache against git config, and skip the
probe unless `upstream` is present.

* fix(git): avoid stale remote probe cache entries

* fix(github): observe origin repository probe failures

* fix(github): observe verified origin probe failures

* fix(github): skip missing upstream probe for PR lists

* test(github): scope the #9171 lazy-resolution guard to default-branch commands

The guard asserted that no git command runs for an open PR, using "no git at
all" as a proxy for "no default-branch resolution". Remote-name listing is a
separate concern, so allow it and keep every other command forbidden; the
symbolic-ref/rev-parse resolution this issue is about stays unreachable.

---------

Co-authored-by: Neil <neil@stably.ai>
This commit is contained in:
Wooseong Kim
2026-09-13 20:18:22 -07:00
committed by GitHub
co-authored by Neil
parent 16d1ab81d3
commit 31db2774f8
13 changed files with 1016 additions and 241 deletions
+154 -2
View File
@@ -8,13 +8,15 @@ const {
getOwnerRepoMock,
getOwnerRepoForRemoteMock,
getSshGitProviderGenerationMock,
isGitHubHostAuthenticatedMock
isGitHubHostAuthenticatedMock,
shouldProbeGitRemoteMock
} = vi.hoisted(() => ({
getEnterpriseGitHubRepoSlugMock: vi.fn(),
getOwnerRepoMock: vi.fn(),
getOwnerRepoForRemoteMock: vi.fn(),
getSshGitProviderGenerationMock: vi.fn(() => 0),
isGitHubHostAuthenticatedMock: vi.fn()
isGitHubHostAuthenticatedMock: vi.fn(),
shouldProbeGitRemoteMock: vi.fn(async () => true)
}))
vi.mock('../providers/ssh-git-dispatch', async (importOriginal) => ({
@@ -35,12 +37,18 @@ vi.mock('./github-enterprise-repository', async (importOriginal) => ({
isGitHubHostAuthenticated: isGitHubHostAuthenticatedMock
}))
vi.mock('../git/remote-name-listing', () => ({
shouldProbeGitRemote: shouldProbeGitRemoteMock
}))
import {
_resetOriginGitHubApiRepositoryCache,
getGitHubApiRepositoryForRemote,
getIssueGitHubApiRepository,
getOriginGitHubApiRepository,
githubHostExecOptions,
resolveGitHubApiRepository,
resolveGitHubApiRepositoryCandidates,
resolveGitHubRepoExecution
} from './github-api-repository'
@@ -51,6 +59,7 @@ beforeEach(() => {
getOwnerRepoForRemoteMock.mockReset().mockResolvedValue(null)
getSshGitProviderGenerationMock.mockReset().mockReturnValue(0)
isGitHubHostAuthenticatedMock.mockReset().mockResolvedValue(false)
shouldProbeGitRemoteMock.mockReset().mockResolvedValue(true)
})
describe('githubHostExecOptions', () => {
@@ -346,3 +355,146 @@ describe('origin repository cache', () => {
expect(getEnterpriseGitHubRepoSlugMock).toHaveBeenCalledTimes(2)
})
})
describe('skip missing upstream remote probes', () => {
it('starts the issue origin probe before checking whether upstream exists', async () => {
let releaseRemoteProbe: (value: boolean) => void = () => undefined
shouldProbeGitRemoteMock.mockReturnValue(
new Promise<boolean>((resolve) => {
releaseRemoteProbe = resolve
})
)
let originStarted = false
getOwnerRepoForRemoteMock.mockImplementation(async (_path, remote) => {
if (remote === 'origin') {
originStarted = true
return { owner: 'fork', repo: 'orca' }
}
return { owner: 'stablyai', repo: 'orca' }
})
const resultPromise = getIssueGitHubApiRepository('/repo')
expect(originStarted).toBe(true)
releaseRemoteProbe(true)
await expect(resultPromise).resolves.toEqual({
owner: 'stablyai',
repo: 'orca',
host: 'github.com'
})
})
it('does not probe upstream for issue identity when that remote is absent', async () => {
shouldProbeGitRemoteMock.mockResolvedValue(false)
getOwnerRepoForRemoteMock.mockImplementation(async (_path, remote) =>
remote === 'origin' ? { owner: 'acme', repo: 'widgets' } : null
)
await expect(getIssueGitHubApiRepository('/repo')).resolves.toEqual({
owner: 'acme',
repo: 'widgets',
host: 'github.com'
})
expect(getOwnerRepoForRemoteMock.mock.calls.map(([, remote]) => remote)).toEqual(['origin'])
})
it('still probes upstream for issue identity when that remote is present', async () => {
getOwnerRepoForRemoteMock.mockImplementation(async (_path, remote) =>
remote === 'upstream' ? { owner: 'stablyai', repo: 'orca' } : { owner: 'fork', repo: 'orca' }
)
await expect(getIssueGitHubApiRepository('/repo')).resolves.toEqual({
owner: 'stablyai',
repo: 'orca',
host: 'github.com'
})
expect(getOwnerRepoForRemoteMock).toHaveBeenCalledWith('/repo', 'upstream', undefined, {})
})
it('observes a rejected origin probe when upstream resolves the issue repository', async () => {
const originError = new Error('origin probe failed')
getOwnerRepoForRemoteMock.mockImplementation(async (_path, remote) => {
if (remote === 'origin') {
throw originError
}
return { owner: 'stablyai', repo: 'orca' }
})
await expect(getIssueGitHubApiRepository('/repo')).resolves.toEqual({
owner: 'stablyai',
repo: 'orca',
host: 'github.com'
})
})
it('preserves a rejected origin probe when upstream cannot resolve the issue repository', async () => {
const originError = new Error('origin probe failed')
getOwnerRepoForRemoteMock.mockImplementation(async (_path, remote) => {
if (remote === 'origin') {
throw originError
}
return null
})
await expect(getIssueGitHubApiRepository('/repo')).rejects.toBe(originError)
})
it('does not probe upstream for PR candidates when that remote is absent', async () => {
shouldProbeGitRemoteMock.mockResolvedValue(false)
getOwnerRepoForRemoteMock.mockResolvedValue({ owner: 'fork', repo: 'orca' })
await expect(resolveGitHubApiRepositoryCandidates('/repo')).resolves.toEqual({
candidates: [{ owner: 'fork', repo: 'orca', host: 'github.com' }],
headRepo: { owner: 'fork', repo: 'orca', host: 'github.com' }
})
expect(getOwnerRepoForRemoteMock.mock.calls.map(([, remote]) => remote)).toEqual(['origin'])
})
it('observes and propagates a verified origin probe failure while listing remotes', async () => {
let releaseRemoteProbe: (value: boolean) => void = () => undefined
shouldProbeGitRemoteMock.mockReturnValue(
new Promise<boolean>((resolve) => {
releaseRemoteProbe = resolve
})
)
const originError = new Error('origin probe failed')
getOwnerRepoForRemoteMock.mockImplementation(async (_path, remote) => {
if (remote === 'origin') {
throw originError
}
return { owner: 'stablyai', repo: 'orca' }
})
const resultPromise = resolveGitHubApiRepositoryCandidates('/repo')
await vi.waitFor(() =>
expect(getOwnerRepoForRemoteMock).toHaveBeenCalledWith(
'/repo',
'origin',
undefined,
{},
{ requireVerifiedSshProbe: true }
)
)
releaseRemoteProbe(true)
await expect(resultPromise).rejects.toBe(originError)
})
it('still probes upstream for PR candidates when that remote is present', async () => {
getOwnerRepoForRemoteMock.mockImplementation(async (_path, remote) =>
remote === 'upstream' ? { owner: 'Acme', repo: 'Orca' } : { owner: 'acme', repo: 'orca' }
)
await expect(resolveGitHubApiRepositoryCandidates('/repo')).resolves.toEqual({
candidates: [{ owner: 'Acme', repo: 'Orca', host: 'github.com' }],
headRepo: { owner: 'acme', repo: 'orca', host: 'github.com' }
})
expect(getOwnerRepoForRemoteMock).toHaveBeenCalledWith(
'/repo',
'upstream',
undefined,
{},
{ requireVerifiedSshProbe: true }
)
})
})