diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/azure.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/azure.ts index fe4637b971..ac49a9e4c8 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/azure.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/azure.ts @@ -15,22 +15,48 @@ */ import { PublisherBase, PublisherOptions, PublisherResult } from './types'; -import { GitApi } from 'azure-devops-node-api/GitApi'; +import { IGitApi } from 'azure-devops-node-api/GitApi'; import { GitRepositoryCreateOptions } from 'azure-devops-node-api/interfaces/GitInterfaces'; -import { JsonValue, Config } from '@backstage/config'; -import { RequiredTemplateValues } from '../templater'; -import { initRepoAndPush } from './helpers'; import { Config } from '@backstage/config'; +import { initRepoAndPush } from './helpers'; import { Logger } from 'winston'; +import { + AzureIntegrationConfig, + readAzureIntegrationConfigs, +} from '@backstage/integration'; +import gitUrlParse from 'git-url-parse'; +import { getPersonalAccessTokenHandler, WebApi } from 'azure-devops-node-api'; + export class AzurePublisher implements PublisherBase { - private readonly client: GitApi; - private readonly token: string; - private readonly logger: Logger; + private readonly integrations: AzureIntegrationConfig[]; + private readonly apiBaseUrl?: string; + private readonly token?: string; constructor(config: Config, { logger }: { logger: Logger }) { - this.logger = logger; - this.client = client; - this.token = token; + this.integrations = readAzureIntegrationConfigs( + config.getOptionalConfigArray('integrations.azure') ?? [], + ); + + if (!this.integrations.length) { + logger.warn( + 'Integrations for Azure in Scaffolder are not set. This will cause errors in a future release. Please migrate to using integrations config and specifying tokens under hostnames', + ); + } + + this.token = config.getOptionalString('scaffolder.azure.api.token'); + if (this.token) { + logger.warn( + "DEPRECATION: Using the token format under 'scaffolder.github.api.token' will not be respected in future releases. Please consider using integrations config instead", + ); + } + + this.apiBaseUrl = config.getOptionalString('scaffolder.azure.api.baseUrl'); + + if (this.apiBaseUrl) { + logger.warn( + "DEPRECATION: Using the apiBaseUrl format under 'scaffolder.azure.api.baseUrl' will not be respected in future releases. Please consider using integrations config instead", + ); + } } async publish({ @@ -38,7 +64,25 @@ export class AzurePublisher implements PublisherBase { directory, logger, }: PublisherOptions): Promise { - const remoteUrl = await this.createRemote(values); + const { resource: host, owner, name } = gitUrlParse(values.storePath); + + const token = this.getToken(host); + if (!token) { + throw new Error('No token provided to create the remote repository'); + } + const baseUrl = this.getBaseUrl(host); + if (!baseUrl) { + throw new Error('No baseUrl provided to create the remote repository'); + } + + const authHandler = getPersonalAccessTokenHandler(token); + const webApi = new WebApi(baseUrl, authHandler); + const azureClient = await webApi.getGitApi(); + + const remoteUrl = await this.createRemote(azureClient, { + project: owner, + name, + }); const catalogInfoUrl = `${remoteUrl}?path=%2Fcatalog-info.yaml`; await initRepoAndPush({ @@ -46,7 +90,7 @@ export class AzurePublisher implements PublisherBase { remoteUrl, auth: { username: 'notempty', - password: this.token, + password: token, }, logger, }); @@ -55,13 +99,24 @@ export class AzurePublisher implements PublisherBase { } private async createRemote( - values: RequiredTemplateValues & Record, + client: IGitApi, + opts: { name: string; project: string }, ) { - const [project, name] = values.storePath.split('/'); - + // const [project, name] = values.storePath.split('/'); + const { name, project } = opts; const createOptions: GitRepositoryCreateOptions = { name }; - const repo = await this.client.createRepository(createOptions, project); + const repo = await client.createRepository(createOptions, project); return repo.remoteUrl || ''; } + + private getToken(host: string): string | undefined { + return this.token || this.integrations.find(c => c.host === host)?.token; + } + + private getBaseUrl(host: string): string | undefined { + return ( + this.apiBaseUrl || this.integrations.find(c => c.host === host)?.host + ); + } } diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/publishers.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/publishers.ts index 15e350c450..3d6c557714 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/publishers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/publishers.ts @@ -118,14 +118,7 @@ export class Publishers implements PublisherBuilder { const azureConfig = config.getOptionalConfig('scaffolder.azure'); if (azureConfig) { try { - const baseUrl = azureConfig.getString('baseUrl'); - const azureToken = azureConfig.getConfig('api').getString('token'); - - const authHandler = getPersonalAccessTokenHandler(azureToken); - const webApi = new WebApi(baseUrl, authHandler); - const azureClient = await webApi.getGitApi(); - - const azurePublisher = new AzurePublisher(azureClient, azureToken); + const azurePublisher = new AzurePublisher(config, { logger }); publishers.register('azure/api', azurePublisher); } catch (e) { const providerName = 'azure';