diff --git a/.changeset/jolly-islands-fry.md b/.changeset/jolly-islands-fry.md new file mode 100644 index 000000000..65e22306b --- /dev/null +++ b/.changeset/jolly-islands-fry.md @@ -0,0 +1,6 @@ +--- +'@secustor/backstage-plugin-renovate-backend': patch +'@secustor/backstage-plugin-renovate-common': patch +--- + +Modifies getTargetRepo to accept ADO's odd org/\_git/repo form and adds support for ADO as a config option diff --git a/plugins/renovate-backend/README.md b/plugins/renovate-backend/README.md index 28ff06c17..fc1725b97 100644 --- a/plugins/renovate-backend/README.md +++ b/plugins/renovate-backend/README.md @@ -7,6 +7,7 @@ Supported platforms: - Github - Gitlab +- AzureDevOps ## Getting started @@ -45,6 +46,9 @@ integrations: github: - host: github.com token: ${GITHUB_TOKEN} + azure: + - host: dev.azure.com + token: ${AZURE_PAT_TOKEN} backend: cache: @@ -97,6 +101,7 @@ renovate: queue: type: local-fastq + # Can pass any Renovate config through here. config: # only do a lookup and create reports with updates and do not open PRs dryRun: lookup diff --git a/plugins/renovate-backend/src/wrapper/platforms/index.test.ts b/plugins/renovate-backend/src/wrapper/platforms/index.test.ts index af2254dba..896b75d3f 100644 --- a/plugins/renovate-backend/src/wrapper/platforms/index.test.ts +++ b/plugins/renovate-backend/src/wrapper/platforms/index.test.ts @@ -1,6 +1,6 @@ import { getPlatformEnvs } from './index'; import { mockDeep } from 'jest-mock-extended'; -import { DefaultGithubCredentialsProvider } from '@backstage/integration'; +import { DefaultGithubCredentialsProvider, DefaultAzureDevOpsCredentialsProvider } from '@backstage/integration'; import { mockServices } from '@backstage/backend-test-utils'; const githubCredentialProvider = mockDeep(); @@ -8,9 +8,15 @@ DefaultGithubCredentialsProvider.fromIntegrations = jest .fn() .mockReturnValue(githubCredentialProvider); +const azureCredentialProvider = mockDeep(); +DefaultAzureDevOpsCredentialsProvider.fromIntegrations = jest + .fn() + .mockReturnValue(azureCredentialProvider); + describe('wrapper/platforms', () => { beforeEach(() => { githubCredentialProvider.getCredentials.mockReset(); + azureCredentialProvider.getCredentials.mockReset(); }); it('throw if platform could not be identified', async () => { @@ -153,4 +159,53 @@ describe('wrapper/platforms', () => { RENOVATE_TOKEN: 'bbbbbbbbbb', }); }); -}); + + it('return env for azure devops with token', async () => { + const rootConfig = mockServices.rootConfig({ + data: { + integrations: { + github: [ + { + host: 'github.com', + token: 'aaaaaa', + }, + ], + azure: [ + { + host: 'dev.azure.com', + token: 'cccccccc', + }, + ], + }, + }, + }); + azureCredentialProvider.getCredentials.mockResolvedValue({ + headers: { Authorization: 'Basic cccccccc' }, + token: 'cccccccc', + type: 'pat', + }); + azureCredentialProvider.getCredentials.mockResolvedValue({ + headers: { Authorization: 'Basic cccccccc' }, + token: 'cccccccc', + type: 'pat', + }); + await expect( + getPlatformEnvs( + { + host: 'dev.azure.com', + repository: 'myOrg/myRepo', + }, + { + rootConfig, + logger: mockServices.logger.mock(), + }, + ), + ).resolves.toEqual({ + RENOVATE_ENDPOINT: 'https://dev.azure.com', + GITHUB_COM_TOKEN: 'aaaaaa', + RENOVATE_PLATFORM: 'azure', + RENOVATE_REPOSITORIES: 'myOrg/myRepo', + RENOVATE_TOKEN: 'cccccccc', + }); + }); +}); \ No newline at end of file diff --git a/plugins/renovate-backend/src/wrapper/platforms/index.ts b/plugins/renovate-backend/src/wrapper/platforms/index.ts index d546e01be..87e24e883 100644 --- a/plugins/renovate-backend/src/wrapper/platforms/index.ts +++ b/plugins/renovate-backend/src/wrapper/platforms/index.ts @@ -1,6 +1,7 @@ import { DefaultGitlabCredentialsProvider, ScmIntegrations, + DefaultAzureDevOpsCredentialsProvider, } from '@backstage/integration'; import is from '@sindresorhus/is'; import { PlatformEnvsOptions } from './types'; @@ -52,6 +53,17 @@ export async function getPlatformEnvs( env.RENOVATE_REPOSITORIES = target.repository; } break; + case 'azure': + { + const cred = await DefaultAzureDevOpsCredentialsProvider.fromIntegrations( + integrations, + ).getCredentials({ url }); + env.RENOVATE_PLATFORM = 'azure'; + env.RENOVATE_ENDPOINT = `https://${target.host}`; + env.RENOVATE_TOKEN = requireConfigVariable(cred?.token, errMsg); + env.RENOVATE_REPOSITORIES = target.repository; + } + break; default: throw new Error(`Unsupported platform type ${integration.type}`); } diff --git a/plugins/renovate-common/src/utils.test.ts b/plugins/renovate-common/src/utils.test.ts index 9c65a1340..2da768855 100644 --- a/plugins/renovate-common/src/utils.test.ts +++ b/plugins/renovate-common/src/utils.test.ts @@ -107,6 +107,15 @@ describe('utils', () => { }); }); + it('should extract ADO repo format', () => { + const result = getTargetRepo('dev.azure.com/project/_git/repo'); + + expect(result).toMatchObject({ + host: 'dev.azure.com', + repository: 'project/repo', + }); + }); + it('should extract repo from Entity with source location annotation', () => { const entity: Entity = { apiVersion: 'backstage.io/v1alpha1', diff --git a/plugins/renovate-common/src/utils.ts b/plugins/renovate-common/src/utils.ts index 8b1a86b7b..0c1be5bbb 100644 --- a/plugins/renovate-common/src/utils.ts +++ b/plugins/renovate-common/src/utils.ts @@ -20,9 +20,21 @@ export function getTargetRepo( return target; } const url = getTargetURL(target); + + let repository = url.full_name; + + if (repository.includes('/_git/')) { + const parts = repository.split('/_git/'); + const leftSide = parts[0].split('/'); + const repoName = parts[1]; + const projectName = leftSide[leftSide.length - 1]; + + repository = `${projectName}/${repoName}`; + } + return { host: url.resource, - repository: url.full_name, + repository: repository, }; }