diff --git a/lib/platform/azure/__snapshots__/index.spec.ts.snap b/lib/platform/azure/__snapshots__/index.spec.ts.snap index 1b1f3961bba9a4..f086e94fa491ae 100644 --- a/lib/platform/azure/__snapshots__/index.spec.ts.snap +++ b/lib/platform/azure/__snapshots__/index.spec.ts.snap @@ -181,6 +181,15 @@ Object { exports[`platform/azure getBranchPr(branchName) should return the pr 1`] = `null`; +exports[`platform/azure getJsonFile() supports fetch from another repo 1`] = ` +Array [ + Array [ + "123456", + "file.json", + ], +] +`; + exports[`platform/azure getPr(prNo) should return a pr in the right format 1`] = ` Object { "body": undefined, diff --git a/lib/platform/azure/index.spec.ts b/lib/platform/azure/index.spec.ts index 27108d2ed39bd7..7c18b91d8087bc 100644 --- a/lib/platform/azure/index.spec.ts +++ b/lib/platform/azure/index.spec.ts @@ -1214,5 +1214,22 @@ describe('platform/azure', () => { ); await expect(azure.getJsonFile('file.json')).rejects.toThrow(); }); + it('supports fetch from another repo', async () => { + const data = { foo: 'bar' }; + const gitApiMock = { + getItemContent: jest.fn(() => + Promise.resolve(Readable.from(JSON.stringify(data))) + ), + getRepositories: jest.fn(() => + Promise.resolve([ + { id: '123456', name: 'bar', project: { name: 'foo' } }, + ]) + ), + }; + azureApi.gitApi.mockImplementationOnce(() => gitApiMock as any); + const res = await azure.getJsonFile('file.json', 'foo/bar'); + expect(res).toEqual(data); + expect(gitApiMock.getItemContent.mock.calls).toMatchSnapshot(); + }); }); }); diff --git a/lib/platform/azure/index.ts b/lib/platform/azure/index.ts index 20592a34a76516..5b0bc3bab7c375 100644 --- a/lib/platform/azure/index.ts +++ b/lib/platform/azure/index.ts @@ -42,6 +42,7 @@ import { getNewBranchName, getProjectAndRepo, getRenovatePRFormat, + getRepoByName, getStorageExtraCloneOpts, max4000Chars, streamToString, @@ -109,19 +110,29 @@ export async function getRepos(): Promise { export async function getRawFile( fileName: string, - repo: string = config.repoId + repoName?: string ): Promise { const azureApiGit = await azureApi.gitApi(); - const buf = await azureApiGit.getItemContent(repo, fileName); + + let repoId: string; + if (repoName) { + const repos = await azureApiGit.getRepositories(); + const repo = getRepoByName(repoName, repos); + repoId = repo.id; + } else { + repoId = config.repoId; + } + + const buf = await azureApiGit.getItemContent(repoId, fileName); const str = await streamToString(buf); return str; } export async function getJsonFile( fileName: string, - repo: string = config.repoId + repoName?: string ): Promise { - const raw = await getRawFile(fileName, repo); + const raw = await getRawFile(fileName, repoName); return JSON.parse(raw); } @@ -134,12 +145,7 @@ export async function initRepo({ config = { repository } as Config; const azureApiGit = await azureApi.gitApi(); const repos = await azureApiGit.getRepositories(); - const names = getProjectAndRepo(repository); - const repo = repos.filter( - (c) => - c.name.toLowerCase() === names.repo.toLowerCase() && - c.project.name.toLowerCase() === names.project.toLowerCase() - )[0]; + const repo = getRepoByName(repository, repos); logger.debug({ repositoryDetails: repo }, 'Repository details'); // istanbul ignore if if (!repo.defaultBranch) { @@ -153,6 +159,7 @@ export async function initRepo({ const defaultBranch = repo.defaultBranch.replace('refs/heads/', ''); config.defaultBranch = defaultBranch; logger.debug(`${repository} default branch = ${defaultBranch}`); + const names = getProjectAndRepo(repository); config.defaultMergeMethod = await azureHelper.getMergeMethod( repo.id, names.project diff --git a/lib/platform/azure/util.spec.ts b/lib/platform/azure/util.spec.ts index 48df6ad0e396da..c816ea2229db6d 100644 --- a/lib/platform/azure/util.spec.ts +++ b/lib/platform/azure/util.spec.ts @@ -6,6 +6,7 @@ import { getNewBranchName, getProjectAndRepo, getRenovatePRFormat, + getRepoByName, getStorageExtraCloneOpts, max4000Chars, streamToString, @@ -178,4 +179,51 @@ describe('platform/azure/helpers', () => { ); }); }); + + describe('getRepoByName', () => { + it('returns null when repos array is empty', () => { + expect(getRepoByName('foo/bar', [])).toBeNull(); + expect(getRepoByName('foo/bar', undefined)).toBeNull(); + expect(getRepoByName('foo/bar', null)).toBeNull(); + }); + it('returns null when repo is not found', () => { + expect( + getRepoByName('foo/foo', [{ name: 'bar', project: { name: 'bar' } }]) + ).toBeNull(); + }); + it('finds repo', () => { + expect( + getRepoByName('foo/bar', [ + { id: '1', name: 'baz', project: { name: 'qux' } }, + null, + undefined, + { id: '2', name: 'bar' }, + { id: '3', name: 'bar', project: { name: 'foo' } }, + { id: '4', name: 'bar', project: { name: 'foo' } }, + ]) + ).toMatchObject({ id: '3' }); + }); + it('supports shorthand names', () => { + expect( + getRepoByName('foo', [ + { id: '1', name: 'bar', project: { name: 'bar' } }, + { id: '2', name: 'foo', project: { name: 'foo' } }, + ]) + ).toMatchObject({ id: '2' }); + }); + it('is case-independent', () => { + const repos = [ + { id: '1', name: 'FOO', project: { name: 'FOO' } }, + { id: '2', name: 'foo', project: { name: 'foo' } }, + ]; + expect(getRepoByName('FOO/foo', repos)).toMatchObject({ id: '1' }); + expect(getRepoByName('foo/FOO', repos)).toMatchObject({ id: '1' }); + expect(getRepoByName('foo/foo', repos)).toMatchObject({ id: '1' }); + }); + it('throws when repo name is invalid', () => { + expect(() => getRepoByName(undefined, [])).toThrow(); + expect(() => getRepoByName(null, [])).toThrow(); + expect(() => getRepoByName('foo/bar/baz', [])).toThrow(); + }); + }); }); diff --git a/lib/platform/azure/util.ts b/lib/platform/azure/util.ts index d2807a28086aa4..7a5fce052a4296 100644 --- a/lib/platform/azure/util.ts +++ b/lib/platform/azure/util.ts @@ -1,5 +1,6 @@ import { GitPullRequest, + GitRepository, GitStatusContext, PullRequestAsyncStatus, PullRequestStatus, @@ -182,3 +183,22 @@ export function getProjectAndRepo( logger.error(msg); throw new Error(msg); } + +export function getRepoByName( + name: string, + repos: GitRepository[] +): GitRepository | null { + logger.trace(`getRepoByName(${name})`); + + let { project, repo } = getProjectAndRepo(name); + project = project.toLowerCase(); + repo = repo.toLowerCase(); + + return ( + repos?.find( + (r) => + project === r?.project?.name?.toLowerCase() && + repo === r?.name?.toLowerCase() + ) || null + ); +}