From dcd5a93a96e13d8b5bbc338ad6c5e907c0813026 Mon Sep 17 00:00:00 2001 From: Phil Kuang Date: Thu, 22 Apr 2021 20:25:01 -0400 Subject: [PATCH 1/2] fix(locationAnalyzer): don't add github slug annotation when SCM system cannot be determined Signed-off-by: Phil Kuang --- .changeset/eleven-donkeys-agree.md | 5 +++++ .changeset/gold-boats-wash.md | 5 +++++ packages/integration/api-report.md | 5 +++++ .../src/azure/AzureIntegration.test.ts | 1 + .../integration/src/azure/AzureIntegration.ts | 7 +++++++ .../bitbucket/BitbucketIntegration.test.ts | 1 + .../src/bitbucket/BitbucketIntegration.ts | 7 +++++++ .../src/github/GitHubIntegration.test.ts | 1 + .../src/github/GitHubIntegration.ts | 7 +++++++ .../src/gitlab/GitLabIntegration.test.ts | 1 + .../src/gitlab/GitLabIntegration.ts | 7 +++++++ packages/integration/src/types.ts | 5 +++++ .../src/ingestion/LocationAnalyzer.ts | 19 ++++++++++++++----- .../src/next/NextCatalogBuilder.ts | 2 +- .../src/service/CatalogBuilder.ts | 3 ++- 15 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 .changeset/eleven-donkeys-agree.md create mode 100644 .changeset/gold-boats-wash.md diff --git a/.changeset/eleven-donkeys-agree.md b/.changeset/eleven-donkeys-agree.md new file mode 100644 index 0000000000..978094095d --- /dev/null +++ b/.changeset/eleven-donkeys-agree.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Correctly add `/project-slug` annotation for new catalog-info.yaml PRs based on SCM integration. diff --git a/.changeset/gold-boats-wash.md b/.changeset/gold-boats-wash.md new file mode 100644 index 0000000000..709df2baf9 --- /dev/null +++ b/.changeset/gold-boats-wash.md @@ -0,0 +1,5 @@ +--- +'@backstage/integration': patch +--- + +Expose an `annotationPrefix` property for SCM integrations diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index 5387eb12db..09d14a5a11 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -9,6 +9,7 @@ import { Config } from '@backstage/config'; // @public (undocumented) export class AzureIntegration implements ScmIntegration { constructor(integrationConfig: AzureIntegrationConfig); + get annotationPrefix(): string; // (undocumented) get config(): AzureIntegrationConfig; // (undocumented) @@ -36,6 +37,7 @@ export type AzureIntegrationConfig = { // @public (undocumented) export class BitbucketIntegration implements ScmIntegration { constructor(integrationConfig: BitbucketIntegrationConfig); + get annotationPrefix(): string; // (undocumented) get config(): BitbucketIntegrationConfig; // (undocumented) @@ -118,6 +120,7 @@ export class GithubCredentialsProvider { // @public (undocumented) export class GitHubIntegration implements ScmIntegration { constructor(integrationConfig: GitHubIntegrationConfig); + get annotationPrefix(): string; // (undocumented) get config(): GitHubIntegrationConfig; // (undocumented) @@ -148,6 +151,7 @@ export type GitHubIntegrationConfig = { // @public (undocumented) export class GitLabIntegration implements ScmIntegration { constructor(integrationConfig: GitLabIntegrationConfig); + get annotationPrefix(): string; // (undocumented) get config(): GitLabIntegrationConfig; // (undocumented) @@ -209,6 +213,7 @@ export function readGoogleGcsIntegrationConfig(config: Config): GoogleGcsIntegra // @public export interface ScmIntegration { + annotationPrefix: string; resolveEditUrl(url: string): string; resolveUrl(options: { url: string; diff --git a/packages/integration/src/azure/AzureIntegration.test.ts b/packages/integration/src/azure/AzureIntegration.test.ts index 1702602cf3..a41ee6ac5f 100644 --- a/packages/integration/src/azure/AzureIntegration.test.ts +++ b/packages/integration/src/azure/AzureIntegration.test.ts @@ -39,6 +39,7 @@ describe('AzureIntegration', () => { it('returns the basics', () => { const integration = new AzureIntegration({ host: 'h.com' } as any); expect(integration.type).toBe('azure'); + expect(integration.annotationPrefix).toBe('dev.azure.com'); expect(integration.title).toBe('h.com'); }); diff --git a/packages/integration/src/azure/AzureIntegration.ts b/packages/integration/src/azure/AzureIntegration.ts index 50f4757620..d0f6f54ede 100644 --- a/packages/integration/src/azure/AzureIntegration.ts +++ b/packages/integration/src/azure/AzureIntegration.ts @@ -36,6 +36,13 @@ export class AzureIntegration implements ScmIntegration { return 'azure'; } + /** + * The prefix used for entity metadata.annotations for the integration. + */ + get annotationPrefix(): string { + return 'dev.azure.com'; + } + get title(): string { return this.integrationConfig.host; } diff --git a/packages/integration/src/bitbucket/BitbucketIntegration.test.ts b/packages/integration/src/bitbucket/BitbucketIntegration.test.ts index 350bb1d63c..27adbd1a8f 100644 --- a/packages/integration/src/bitbucket/BitbucketIntegration.test.ts +++ b/packages/integration/src/bitbucket/BitbucketIntegration.test.ts @@ -42,6 +42,7 @@ describe('BitbucketIntegration', () => { it('returns the basics', () => { const integration = new BitbucketIntegration({ host: 'h.com' } as any); expect(integration.type).toBe('bitbucket'); + expect(integration.annotationPrefix).toBe('bitbucket.org'); expect(integration.title).toBe('h.com'); }); diff --git a/packages/integration/src/bitbucket/BitbucketIntegration.ts b/packages/integration/src/bitbucket/BitbucketIntegration.ts index 1e07507bf2..d23a78986a 100644 --- a/packages/integration/src/bitbucket/BitbucketIntegration.ts +++ b/packages/integration/src/bitbucket/BitbucketIntegration.ts @@ -41,6 +41,13 @@ export class BitbucketIntegration implements ScmIntegration { return 'bitbucket'; } + /** + * The prefix used for entity metadata.annotations for the integration. + */ + get annotationPrefix(): string { + return 'bitbucket.org'; + } + get title(): string { return this.integrationConfig.host; } diff --git a/packages/integration/src/github/GitHubIntegration.test.ts b/packages/integration/src/github/GitHubIntegration.test.ts index 501ae6c3bf..f224a169ce 100644 --- a/packages/integration/src/github/GitHubIntegration.test.ts +++ b/packages/integration/src/github/GitHubIntegration.test.ts @@ -46,6 +46,7 @@ describe('GitHubIntegration', () => { token: 't', }); expect(integration.type).toBe('github'); + expect(integration.annotationPrefix).toBe('github.com'); expect(integration.title).toBe('h.com'); expect(integration.config.host).toBe('h.com'); }); diff --git a/packages/integration/src/github/GitHubIntegration.ts b/packages/integration/src/github/GitHubIntegration.ts index d33057b88f..3b88233cad 100644 --- a/packages/integration/src/github/GitHubIntegration.ts +++ b/packages/integration/src/github/GitHubIntegration.ts @@ -38,6 +38,13 @@ export class GitHubIntegration implements ScmIntegration { return 'github'; } + /** + * The prefix used for entity metadata.annotations for the integration. + */ + get annotationPrefix(): string { + return 'github.com'; + } + get title(): string { return this.integrationConfig.host; } diff --git a/packages/integration/src/gitlab/GitLabIntegration.test.ts b/packages/integration/src/gitlab/GitLabIntegration.test.ts index 5cc6d410e3..e70b6ea094 100644 --- a/packages/integration/src/gitlab/GitLabIntegration.test.ts +++ b/packages/integration/src/gitlab/GitLabIntegration.test.ts @@ -41,6 +41,7 @@ describe('GitLabIntegration', () => { it('returns the basics', () => { const integration = new GitLabIntegration({ host: 'h.com' } as any); expect(integration.type).toBe('gitlab'); + expect(integration.annotationPrefix).toBe('gitlab.com'); expect(integration.title).toBe('h.com'); }); diff --git a/packages/integration/src/gitlab/GitLabIntegration.ts b/packages/integration/src/gitlab/GitLabIntegration.ts index 17fb82f010..fd356581a5 100644 --- a/packages/integration/src/gitlab/GitLabIntegration.ts +++ b/packages/integration/src/gitlab/GitLabIntegration.ts @@ -38,6 +38,13 @@ export class GitLabIntegration implements ScmIntegration { return 'gitlab'; } + /** + * The prefix used for entity metadata.annotations for the integration. + */ + get annotationPrefix(): string { + return 'gitlab.com'; + } + get title(): string { return this.integrationConfig.host; } diff --git a/packages/integration/src/types.ts b/packages/integration/src/types.ts index f06bda285a..971b341a8e 100644 --- a/packages/integration/src/types.ts +++ b/packages/integration/src/types.ts @@ -29,6 +29,11 @@ export interface ScmIntegration { */ type: string; + /** + * The prefix used for entity metadata.annotations for the integration , e.g. "github.com". + */ + annotationPrefix: string; + /** * A human readable title for the integration, that can be shown to users to * differentiate between different integrations. diff --git a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts index bc81cf14f8..f510bec37e 100644 --- a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts +++ b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts @@ -16,6 +16,8 @@ import { Logger } from 'winston'; import parseGitUrl from 'git-url-parse'; +import { Entity } from '@backstage/catalog-model'; +import { ScmIntegrationRegistry } from '@backstage/integration'; import { AnalyzeLocationRequest, AnalyzeLocationResponse, @@ -24,25 +26,32 @@ import { export class RepoLocationAnalyzer implements LocationAnalyzer { private readonly logger: Logger; + private readonly scmIntegrations: ScmIntegrationRegistry; - constructor(logger: Logger) { + constructor(logger: Logger, scmIntegrations: ScmIntegrationRegistry) { this.logger = logger; + this.scmIntegrations = scmIntegrations; } async analyzeLocation( request: AnalyzeLocationRequest, ): Promise { - const { owner, name, source } = parseGitUrl(request.location.target); - const entity = { + const { owner, name } = parseGitUrl(request.location.target); + const entity: Entity = { apiVersion: 'backstage.io/v1alpha1', kind: 'Component', metadata: { name: name, - // Probably won't handle properly self-hosted git providers with custom url - annotations: { [`${source}/project-slug`]: `${owner}/${name}` }, }, spec: { type: 'other', lifecycle: 'unknown' }, }; + const integration = this.scmIntegrations.byUrl(request.location.target); + if (integration) { + entity.metadata.annotations = { + [`${integration.annotationPrefix}/project-slug`]: `${owner}/${name}`, + }; + } + this.logger.debug(`entity created for ${request.location.target}`); return { existingEntityFiles: [], diff --git a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts index 43f321248e..e10d13c129 100644 --- a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts +++ b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts @@ -275,7 +275,7 @@ export class NextCatalogBuilder { ); const locationsCatalog = new DatabaseLocationsCatalog(db); - const locationAnalyzer = new RepoLocationAnalyzer(logger); + const locationAnalyzer = new RepoLocationAnalyzer(logger, integrations); const locationService = new DefaultLocationService( locationStore, orchestrator, diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index b8d8b3ee61..f742e3c0db 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -228,6 +228,7 @@ export class CatalogBuilder { locationAnalyzer: LocationAnalyzer; }> { const { config, database, logger } = this.env; + const integrations = ScmIntegrations.fromConfig(config); const policy = this.buildEntityPolicy(); const processors = this.buildProcessors(); @@ -255,7 +256,7 @@ export class CatalogBuilder { locationReader, logger, ); - const locationAnalyzer = new RepoLocationAnalyzer(logger); + const locationAnalyzer = new RepoLocationAnalyzer(logger, integrations); return { entitiesCatalog, From c128e64ce3ad3bf8646f3c3a224d123d73ccbf9c Mon Sep 17 00:00:00 2001 From: Phil Kuang Date: Wed, 12 May 2021 15:06:25 -0400 Subject: [PATCH 2/2] revert(integration): remove annotation prefix props Signed-off-by: Phil Kuang --- .changeset/gold-boats-wash.md | 5 ----- packages/integration/api-report.md | 5 ----- .../src/azure/AzureIntegration.test.ts | 1 - .../integration/src/azure/AzureIntegration.ts | 7 ------ .../bitbucket/BitbucketIntegration.test.ts | 1 - .../src/bitbucket/BitbucketIntegration.ts | 7 ------ .../src/github/GitHubIntegration.test.ts | 1 - .../src/github/GitHubIntegration.ts | 7 ------ .../src/gitlab/GitLabIntegration.test.ts | 1 - .../src/gitlab/GitLabIntegration.ts | 7 ------ packages/integration/src/types.ts | 5 ----- .../src/ingestion/LocationAnalyzer.ts | 22 +++++++++++++++++-- 12 files changed, 20 insertions(+), 49 deletions(-) delete mode 100644 .changeset/gold-boats-wash.md diff --git a/.changeset/gold-boats-wash.md b/.changeset/gold-boats-wash.md deleted file mode 100644 index 709df2baf9..0000000000 --- a/.changeset/gold-boats-wash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/integration': patch ---- - -Expose an `annotationPrefix` property for SCM integrations diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index 09d14a5a11..5387eb12db 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -9,7 +9,6 @@ import { Config } from '@backstage/config'; // @public (undocumented) export class AzureIntegration implements ScmIntegration { constructor(integrationConfig: AzureIntegrationConfig); - get annotationPrefix(): string; // (undocumented) get config(): AzureIntegrationConfig; // (undocumented) @@ -37,7 +36,6 @@ export type AzureIntegrationConfig = { // @public (undocumented) export class BitbucketIntegration implements ScmIntegration { constructor(integrationConfig: BitbucketIntegrationConfig); - get annotationPrefix(): string; // (undocumented) get config(): BitbucketIntegrationConfig; // (undocumented) @@ -120,7 +118,6 @@ export class GithubCredentialsProvider { // @public (undocumented) export class GitHubIntegration implements ScmIntegration { constructor(integrationConfig: GitHubIntegrationConfig); - get annotationPrefix(): string; // (undocumented) get config(): GitHubIntegrationConfig; // (undocumented) @@ -151,7 +148,6 @@ export type GitHubIntegrationConfig = { // @public (undocumented) export class GitLabIntegration implements ScmIntegration { constructor(integrationConfig: GitLabIntegrationConfig); - get annotationPrefix(): string; // (undocumented) get config(): GitLabIntegrationConfig; // (undocumented) @@ -213,7 +209,6 @@ export function readGoogleGcsIntegrationConfig(config: Config): GoogleGcsIntegra // @public export interface ScmIntegration { - annotationPrefix: string; resolveEditUrl(url: string): string; resolveUrl(options: { url: string; diff --git a/packages/integration/src/azure/AzureIntegration.test.ts b/packages/integration/src/azure/AzureIntegration.test.ts index a41ee6ac5f..1702602cf3 100644 --- a/packages/integration/src/azure/AzureIntegration.test.ts +++ b/packages/integration/src/azure/AzureIntegration.test.ts @@ -39,7 +39,6 @@ describe('AzureIntegration', () => { it('returns the basics', () => { const integration = new AzureIntegration({ host: 'h.com' } as any); expect(integration.type).toBe('azure'); - expect(integration.annotationPrefix).toBe('dev.azure.com'); expect(integration.title).toBe('h.com'); }); diff --git a/packages/integration/src/azure/AzureIntegration.ts b/packages/integration/src/azure/AzureIntegration.ts index d0f6f54ede..50f4757620 100644 --- a/packages/integration/src/azure/AzureIntegration.ts +++ b/packages/integration/src/azure/AzureIntegration.ts @@ -36,13 +36,6 @@ export class AzureIntegration implements ScmIntegration { return 'azure'; } - /** - * The prefix used for entity metadata.annotations for the integration. - */ - get annotationPrefix(): string { - return 'dev.azure.com'; - } - get title(): string { return this.integrationConfig.host; } diff --git a/packages/integration/src/bitbucket/BitbucketIntegration.test.ts b/packages/integration/src/bitbucket/BitbucketIntegration.test.ts index 27adbd1a8f..350bb1d63c 100644 --- a/packages/integration/src/bitbucket/BitbucketIntegration.test.ts +++ b/packages/integration/src/bitbucket/BitbucketIntegration.test.ts @@ -42,7 +42,6 @@ describe('BitbucketIntegration', () => { it('returns the basics', () => { const integration = new BitbucketIntegration({ host: 'h.com' } as any); expect(integration.type).toBe('bitbucket'); - expect(integration.annotationPrefix).toBe('bitbucket.org'); expect(integration.title).toBe('h.com'); }); diff --git a/packages/integration/src/bitbucket/BitbucketIntegration.ts b/packages/integration/src/bitbucket/BitbucketIntegration.ts index d23a78986a..1e07507bf2 100644 --- a/packages/integration/src/bitbucket/BitbucketIntegration.ts +++ b/packages/integration/src/bitbucket/BitbucketIntegration.ts @@ -41,13 +41,6 @@ export class BitbucketIntegration implements ScmIntegration { return 'bitbucket'; } - /** - * The prefix used for entity metadata.annotations for the integration. - */ - get annotationPrefix(): string { - return 'bitbucket.org'; - } - get title(): string { return this.integrationConfig.host; } diff --git a/packages/integration/src/github/GitHubIntegration.test.ts b/packages/integration/src/github/GitHubIntegration.test.ts index f224a169ce..501ae6c3bf 100644 --- a/packages/integration/src/github/GitHubIntegration.test.ts +++ b/packages/integration/src/github/GitHubIntegration.test.ts @@ -46,7 +46,6 @@ describe('GitHubIntegration', () => { token: 't', }); expect(integration.type).toBe('github'); - expect(integration.annotationPrefix).toBe('github.com'); expect(integration.title).toBe('h.com'); expect(integration.config.host).toBe('h.com'); }); diff --git a/packages/integration/src/github/GitHubIntegration.ts b/packages/integration/src/github/GitHubIntegration.ts index 3b88233cad..d33057b88f 100644 --- a/packages/integration/src/github/GitHubIntegration.ts +++ b/packages/integration/src/github/GitHubIntegration.ts @@ -38,13 +38,6 @@ export class GitHubIntegration implements ScmIntegration { return 'github'; } - /** - * The prefix used for entity metadata.annotations for the integration. - */ - get annotationPrefix(): string { - return 'github.com'; - } - get title(): string { return this.integrationConfig.host; } diff --git a/packages/integration/src/gitlab/GitLabIntegration.test.ts b/packages/integration/src/gitlab/GitLabIntegration.test.ts index e70b6ea094..5cc6d410e3 100644 --- a/packages/integration/src/gitlab/GitLabIntegration.test.ts +++ b/packages/integration/src/gitlab/GitLabIntegration.test.ts @@ -41,7 +41,6 @@ describe('GitLabIntegration', () => { it('returns the basics', () => { const integration = new GitLabIntegration({ host: 'h.com' } as any); expect(integration.type).toBe('gitlab'); - expect(integration.annotationPrefix).toBe('gitlab.com'); expect(integration.title).toBe('h.com'); }); diff --git a/packages/integration/src/gitlab/GitLabIntegration.ts b/packages/integration/src/gitlab/GitLabIntegration.ts index fd356581a5..17fb82f010 100644 --- a/packages/integration/src/gitlab/GitLabIntegration.ts +++ b/packages/integration/src/gitlab/GitLabIntegration.ts @@ -38,13 +38,6 @@ export class GitLabIntegration implements ScmIntegration { return 'gitlab'; } - /** - * The prefix used for entity metadata.annotations for the integration. - */ - get annotationPrefix(): string { - return 'gitlab.com'; - } - get title(): string { return this.integrationConfig.host; } diff --git a/packages/integration/src/types.ts b/packages/integration/src/types.ts index 971b341a8e..f06bda285a 100644 --- a/packages/integration/src/types.ts +++ b/packages/integration/src/types.ts @@ -29,11 +29,6 @@ export interface ScmIntegration { */ type: string; - /** - * The prefix used for entity metadata.annotations for the integration , e.g. "github.com". - */ - annotationPrefix: string; - /** * A human readable title for the integration, that can be shown to users to * differentiate between different integrations. diff --git a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts index f510bec37e..cacdf38ffd 100644 --- a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts +++ b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts @@ -46,9 +46,27 @@ export class RepoLocationAnalyzer implements LocationAnalyzer { }; const integration = this.scmIntegrations.byUrl(request.location.target); - if (integration) { + let annotationPrefix; + switch (integration?.type) { + case 'azure': + annotationPrefix = 'dev.azure.com'; + break; + case 'bitbucket': + annotationPrefix = 'bitbucket.org'; + break; + case 'github': + annotationPrefix = 'github.com'; + break; + case 'gitlab': + annotationPrefix = 'gitlab.com'; + break; + default: + break; + } + + if (annotationPrefix) { entity.metadata.annotations = { - [`${integration.annotationPrefix}/project-slug`]: `${owner}/${name}`, + [`${annotationPrefix}/project-slug`]: `${owner}/${name}`, }; }