diff --git a/plugins/scaffolder-backend/src/scaffolder/jobs/processor.ts b/plugins/scaffolder-backend/src/scaffolder/jobs/processor.ts index 25f9b23e59..fc11545e04 100644 --- a/plugins/scaffolder-backend/src/scaffolder/jobs/processor.ts +++ b/plugins/scaffolder-backend/src/scaffolder/jobs/processor.ts @@ -120,6 +120,7 @@ export class JobProcessor implements Processor { // Log to the current stage the error that occurred and fail the stage. stage.status = 'FAILED'; logger.error(`Stage failed with error: ${error.message}`); + logger.error(error.stack); // Throw the error so the job can be failed too. throw error; diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/gitlab.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/gitlab.ts index c592b06807..71a38f1079 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/gitlab.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/gitlab.ts @@ -15,11 +15,10 @@ */ import { PublisherBase, PublisherOptions, PublisherResult } from './types'; -import { Gitlab } from '@gitbeaker/core'; -import { Config, JsonValue } from '@backstage/config'; +import { Gitlab } from '@gitbeaker/node'; +import { Config } from '@backstage/config'; import { Logger } from 'winston'; import { initRepoAndPush } from './helpers'; -import { RequiredTemplateValues } from '../templater'; import gitUrlParse from 'git-url-parse'; import { @@ -67,15 +66,31 @@ export class GitlabPublisher implements PublisherBase { async publish({ values, directory, + logger, }: PublisherOptions): Promise { - const remoteUrl = await this.createRemote(values); - const { host } = new URL(remoteUrl); - const token = this.getToken(host); + 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'); + throw new Error( + 'No authentication set for Gitlab publisher. Creating the remote repository is not possible without a token', + ); } + const baseUrl = this.getBaseUrl(host); + if (!baseUrl) { + throw new Error( + 'No host set for Gitlab publisher. Creating the remote repository is not possible without a host', + ); + } + + const remoteUrl = await this.createRemote({ + host: baseUrl, + owner, + name, + token, + }); + await initRepoAndPush({ dir: directory, remoteUrl, @@ -83,10 +98,14 @@ export class GitlabPublisher implements PublisherBase { username: 'oauth2', password: token, }, - logger: this.logger, + logger, }); - return { remoteUrl }; + const catalogInfoUrl = remoteUrl.replace( + /\.git$/, + '/-/blob/master/catalog-info.yaml', + ); + return { remoteUrl, catalogInfoUrl }; } private getToken(host: string): string | undefined { @@ -103,33 +122,15 @@ export class GitlabPublisher implements PublisherBase { ); } - private getConfig(host: string): { baseUrl?: string; token?: string } { - return { - baseUrl: this.getBaseUrl(host), - token: this.getToken(host), - }; - } - - private async createRemote( - values: RequiredTemplateValues & Record, - ) { - const pathElements = values.storePath.split('/'); - const name = pathElements[pathElements.length - 1]; - pathElements.pop(); - const owner = pathElements.join('/'); - - const { resource: host } = gitUrlParse(values.storePath); - - const config = this.getConfig(host); - - if (!config.token) { - throw new Error( - 'No authentication set for Gitlab publisher. Creating the remote repository is not possible without a token', - ); - } - - const client = new Gitlab({ host: config.baseUrl, token: config.token }); + private async createRemote(opts: { + host: string; + name: string; + owner: string; + token: string; + }) { + const { owner, name, host, token } = opts; + const client = new Gitlab({ host: host, token: token }); let targetNamespace = ((await client.Namespaces.show(owner)) as { id: number; }).id; diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/publishers.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/publishers.ts index 3d6c557714..78cc85a6c4 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/publishers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/publishers.ts @@ -15,13 +15,10 @@ */ import { Logger } from 'winston'; -import { getPersonalAccessTokenHandler, WebApi } from 'azure-devops-node-api'; import { Config } from '@backstage/config'; -import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import { DeprecatedLocationTypeDetector, makeDeprecatedLocationTypeDetector, - parseLocationAnnotation, } from '../helpers'; import { PublisherBase, PublisherBuilder } from './types'; import { RemoteProtocol } from '../types'; @@ -39,32 +36,29 @@ export class Publishers implements PublisherBuilder { this.publisherMap.set(protocol, publisher); } - get(template: TemplateEntityV1alpha1): PublisherBase { - const { protocol, location } = parseLocationAnnotation(template); - const publisher = this.publisherMap.get(protocol); + get(storePath: string, { logger }: { logger: Logger }): PublisherBase { + const protocol = this.typeDetector?.(storePath); - if (!publisher) { - if ((protocol as string) === 'url') { - const type = this.typeDetector?.(location); - const detected = type && this.publisherMap.get(type as RemoteProtocol); - if (detected) { - return detected; - } - if (type) { - throw new Error( - `No publisher configuration available for type '${type}' with url "${location}". ` + - "Make sure you've added appropriate configuration in the 'scaffolder' configuration section", - ); - } else { - throw new Error( - `Failed to detect publisher type. Unable to determine integration type for location "${location}". ` + - "Please add appropriate configuration to the 'integrations' configuration section", - ); - } - } - throw new Error(`No publisher registered for type: "${protocol}"`); + if (!protocol) { + throw new Error( + `No matching publisher detected for "${storePath}". Please make sure this host is registered in the integration config`, + ); } + logger.info( + `Selected publisher ${protocol} for publishing to URL ${storePath}`, + ); + + const publisher = this.publisherMap.get(protocol as RemoteProtocol); + if (!publisher) { + throw new Error( + `Failed to detect publisher type. Unable to determine integration type for location "${location}". ` + + "Please add appropriate configuration to the 'integrations' configuration section", + ); + } + + logger.info(`Selected publisher for protocol ${protocol}`); + return publisher; } @@ -139,15 +133,7 @@ export class Publishers implements PublisherBuilder { ); if (bitbucketConfig) { try { - const baseUrl = bitbucketConfig.getString('host'); - const bitbucketUsername = bitbucketConfig.getString('username'); - const bitbucketToken = bitbucketConfig.getString('token'); - - const bitbucketPublisher = new BitbucketPublisher( - baseUrl, - bitbucketUsername, - bitbucketToken, - ); + const bitbucketPublisher = new BitbucketPublisher(config, { logger }); publishers.register('bitbucket', bitbucketPublisher); } catch (e) { const providerName = 'bitbucket'; diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/types.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/types.ts index 4164a6a602..cb726dfe09 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/types.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import { RequiredTemplateValues } from '../templater'; import { JsonValue } from '@backstage/config'; import { RemoteProtocol } from '../types'; @@ -46,5 +45,5 @@ export type PublisherResult = { export type PublisherBuilder = { register(protocol: RemoteProtocol, publisher: PublisherBase): void; - get(template: TemplateEntityV1alpha1): PublisherBase; + get(storePath: string, { logger }: { logger: Logger }): PublisherBase; }; diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index ffd4d6487c..1ca82d4736 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -154,7 +154,9 @@ export async function createRouter( { name: 'Publish template', handler: async (ctx: StageContext<{ resultDir: string }>) => { - const publisher = publishers.get(ctx.entity); + const publisher = publishers.get(ctx.values.storePath, { + logger: ctx.logger, + }); ctx.logger.info('Will now store the template'); const result = await publisher.publish({ values: ctx.values,