Skip proven other-forge projects in Tasks requests (#34219)

* Skip proven other-forge task repositories at the provider boundary

Revalidate GitLab project identity after remote configuration changes and pin resolved list calls to their host. Preserve unresolved local MR inference and settled partial results; carry additive eligibility metadata for GitHub notices.

Co-authored-by: yasuhiro nishioka <yasu00000@gmail.com>

* Keep task source eligibility scoped to each repository host

* Preserve shared host routing for GitLab task lists

* Cover runtime task source marker replacement through its notice reader

* Share initial GitLab config observation across source resolution

---------

Co-authored-by: yasuhiro nishioka <yasu00000@gmail.com>
This commit is contained in:
Brennan Benson
2026-10-29 14:21:22 -07:00
committed by GitHub
co-authored by yasuhiro nishioka
parent 2e38249a7d
commit d2baeff227
16 changed files with 631 additions and 42 deletions
+26
View File
@@ -163,6 +163,32 @@ describe('listWorkItems', () => {
expect(calls.every(([, options]) => options?.host === 'github.acme-corp.com')).toBe(true)
})
it('marks proven GitLab remotes without listing them', async () => {
getIssueOwnerRepoMock.mockResolvedValue(null)
getOwnerRepoMock.mockResolvedValue(null)
gitExecFileAsyncMock.mockImplementation(async (args: string[]) => ({
stdout: args[1] === '-v' ? 'origin\tgit@gitlab.com:team/project.git (fetch)\n' : 'origin\n'
}))
const result = await listWorkItems('/repo', 24)
expect(result.sources).toMatchObject({ issues: null, prs: null, ineligible: true })
expect(ghExecFileAsyncMock).not.toHaveBeenCalled()
})
it.each([
'origin\tgit@custom.example.com:team/project.git (fetch)\n',
'origin\tgit@gitlab.com:team/project.git (fetch)\nupstream\tgit@github.com:team/project.git (fetch)\n'
])('keeps unproven or mixed remote identity unresolved: %s', async (stdout) => {
getIssueOwnerRepoMock.mockResolvedValue(null)
getOwnerRepoMock.mockResolvedValue(null)
gitExecFileAsyncMock.mockResolvedValue({ stdout })
const result = await listWorkItems('/repo', 24)
expect(result.sources.ineligible).toBeUndefined()
expect(ghExecFileAsyncMock).not.toHaveBeenCalled()
})
it('does not run unscoped work-item queries when a local repository has no GitHub source', async () => {
getIssueOwnerRepoMock.mockResolvedValue(null)
getOwnerRepoMock.mockResolvedValue(null)
@@ -7,6 +7,7 @@ import { resolveIssueGitHubApiRepositorySource } from '../../github-api-reposito
import type { MainWorkItem } from './../map/work-item-field-coercion'
import { normalizeWorkItemPage, resolvePrWorkItemSource } from './work-item-list-request'
import { listRecentWorkItems, listQueriedWorkItems } from './work-item-pages'
import { isTaskRepositoryIneligible } from '../../../git/task-repository-eligibility'
export async function listWorkItems(
repoPath: string,
limit = 24,
@@ -36,6 +37,10 @@ export async function listWorkItems(
])
const issueOwnerRepo = issueResolved.source
const prOwnerRepo = prResolved.source
const ineligible =
!issueOwnerRepo &&
!prOwnerRepo &&
(await isTaskRepositoryIneligible(repoPath, 'github', connectionId, localGitOptions))
await acquire()
try {
// Preserve fetch failures for IPC; an empty fallback would hide per-repo outages.
@@ -75,7 +80,8 @@ export async function listWorkItems(
issues: issueOwnerRepo,
prs: prOwnerRepo,
originCandidate: prResolved.originCandidate,
upstreamCandidate: prResolved.upstreamCandidate
upstreamCandidate: prResolved.upstreamCandidate,
...(ineligible ? { ineligible: true as const } : {})
},
...(errors ? { errors } : {}),
...(issueResolved.fellBack ? { issueSourceFellBack: true } : {})