diff --git a/lib/modules/manager/azure-pipelines/extract.spec.ts b/lib/modules/manager/azure-pipelines/extract.spec.ts index 82ff78295af0195bbcafce433762c17cc413ea07..84b198a997450f03f3abd9b2ce43dc53ee3f3363 100644 --- a/lib/modules/manager/azure-pipelines/extract.spec.ts +++ b/lib/modules/manager/azure-pipelines/extract.spec.ts @@ -1,4 +1,5 @@ import { Fixtures } from '../../../../test/fixtures'; +import { GlobalConfig } from '../../../config/global'; import { AzurePipelinesTasksDatasource } from '../../datasource/azure-pipelines-tasks'; import { extractAzurePipelinesTasks, @@ -19,6 +20,10 @@ const azurePipelinesJobs = Fixtures.get('azure-pipelines-jobs.yaml'); const azurePipelinesSteps = Fixtures.get('azure-pipelines-steps.yaml'); describe('modules/manager/azure-pipelines/extract', () => { + afterEach(() => { + GlobalConfig.reset(); + }); + it('should parse a valid azure-pipelines file', () => { const file = parseAzurePipelines(azurePipelines, azurePipelinesFilename); expect(file).not.toBeNull(); @@ -72,6 +77,53 @@ describe('modules/manager/azure-pipelines/extract', () => { }) ).toBeNull(); }); + + it('should extract Azure repository information if project in name', () => { + GlobalConfig.set({ + platform: 'azure', + endpoint: 'https://dev.azure.com/renovate-org', + }); + + expect( + extractRepository({ + type: 'git', + name: 'project/repo', + ref: 'refs/tags/v1.0.0', + }) + ).toMatchObject({ + depName: 'project/repo', + packageName: 'https://dev.azure.com/renovate-org/project/_git/repo', + }); + }); + + it('should return null if repository type is git and project not in name', () => { + GlobalConfig.set({ + platform: 'azure', + endpoint: 'https://dev.azure.com/renovate-org', + }); + + expect( + extractRepository({ + type: 'git', + name: 'repo', + ref: 'refs/tags/v1.0.0', + }) + ).toBeNull(); + }); + + it('should extract return null for git repo type if platform not Azure', () => { + GlobalConfig.set({ + platform: 'github', + }); + + expect( + extractRepository({ + type: 'git', + name: 'project/repo', + ref: 'refs/tags/v1.0.0', + }) + ).toBeNull(); + }); }); describe('extractContainer()', () => { diff --git a/lib/modules/manager/azure-pipelines/extract.ts b/lib/modules/manager/azure-pipelines/extract.ts index 50cfc49fc4073fc28adf1da4c422742d30b6a336..512ca41124519c3229074da90a988a61e684f05a 100644 --- a/lib/modules/manager/azure-pipelines/extract.ts +++ b/lib/modules/manager/azure-pipelines/extract.ts @@ -1,7 +1,9 @@ import { load } from 'js-yaml'; +import { GlobalConfig } from '../../../config/global'; import { logger } from '../../../logger'; import { coerceArray } from '../../../util/array'; import { regEx } from '../../../util/regex'; +import { joinUrlParts } from '../../../util/url'; import { AzurePipelinesTasksDatasource } from '../../datasource/azure-pipelines-tasks'; import { GitTagsDatasource } from '../../datasource/git-tags'; import { getDep } from '../dockerfile/extract'; @@ -13,7 +15,37 @@ const AzurePipelinesTaskRegex = regEx(/^(?<name>[^@]+)@(?<version>.*)$/); export function extractRepository( repository: Repository ): PackageDependency | null { - if (repository.type !== 'github') { + let repositoryUrl = null; + + if (repository.type === 'github') { + repositoryUrl = `https://github.com/${repository.name}.git`; + } else if (repository.type === 'git') { + // "git" type indicates an AzureDevOps repository. + // The repository URL is only deducible if we are running on AzureDevOps (so can use the endpoint) + // and the name is of the form `Project/Repository`. + // The name could just be the repository name, in which case AzureDevOps defaults to the + // same project, which is not currently accessible here. It could be deduced later by exposing + // the repository URL to managers. + // https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/resources-repositories-repository?view=azure-pipelines#types + const { platform, endpoint } = GlobalConfig.get(); + if (platform === 'azure' && endpoint) { + if (repository.name.includes('/')) { + const [projectName, repoName] = repository.name.split('/'); + repositoryUrl = joinUrlParts( + endpoint, + encodeURIComponent(projectName), + '_git', + encodeURIComponent(repoName) + ); + } else { + logger.debug( + 'Renovate cannot update repositories that do not include the project name' + ); + } + } + } + + if (repositoryUrl === null) { return null; } @@ -27,7 +59,7 @@ export function extractRepository( datasource: GitTagsDatasource.id, depName: repository.name, depType: 'gitTags', - packageName: `https://github.com/${repository.name}.git`, + packageName: repositoryUrl, replaceString: repository.ref, }; }