* Lists the project's git repositories. Returns an empty list on 401/403/404 so * a project without Git or without repo access degrades gracefully instead of * aborting the sync.
( accessToken: string, organization: string, project: string, retryOptions?: Parameters<typeof fetchWithRetry>[2], syncContext?: Record<string, unknown> )
| 681 | * aborting the sync. |
| 682 | */ |
| 683 | async function listRepositories( |
| 684 | accessToken: string, |
| 685 | organization: string, |
| 686 | project: string, |
| 687 | retryOptions?: Parameters<typeof fetchWithRetry>[2], |
| 688 | syncContext?: Record<string, unknown> |
| 689 | ): Promise<GitRepository[]> { |
| 690 | const url = `${ADO_BASE_URL}/${encodeURIComponent(organization)}/${encodeURIComponent(project)}/_apis/git/repositories?api-version=${GIT_API_VERSION}` |
| 691 | const response = await fetchWithRetry( |
| 692 | url, |
| 693 | { |
| 694 | method: 'GET', |
| 695 | headers: { Accept: 'application/json', Authorization: patAuthHeader(accessToken) }, |
| 696 | }, |
| 697 | retryOptions |
| 698 | ) |
| 699 | if (!response.ok) { |
| 700 | if (response.status === 401 || response.status === 403 || response.status === 404) { |
| 701 | /** |
| 702 | * 401/403 mean repositories still exist but this PAT cannot read them |
| 703 | * right now — flag the listing as incomplete so reconciliation does not |
| 704 | * delete previously synced repository files. A 404 means the Git feature |
| 705 | * is genuinely absent, so reconciliation stays enabled. |
| 706 | */ |
| 707 | if ((response.status === 401 || response.status === 403) && syncContext) { |
| 708 | syncContext.listingCapped = true |
| 709 | } |
| 710 | logger.warn('Azure DevOps repositories unavailable; skipping file listing', { |
| 711 | organization, |
| 712 | project, |
| 713 | status: response.status, |
| 714 | }) |
| 715 | return [] |
| 716 | } |
| 717 | const errorText = await response.text().catch(() => '') |
| 718 | logger.error('Failed to list Azure DevOps repositories', { |
| 719 | status: response.status, |
| 720 | error: errorText, |
| 721 | }) |
| 722 | throw new Error(`Failed to list repositories: ${response.status}`) |
| 723 | } |
| 724 | const data = await response.json() |
| 725 | return (data.value as GitRepository[] | undefined) ?? [] |
| 726 | } |
| 727 | |
| 728 | /** |
| 729 | * Resolves the in-scope repositories for the project, caching them on the sync |
no test coverage detected