diff --git a/.changeset/pretty-onions-knock.md b/.changeset/pretty-onions-knock.md new file mode 100644 index 0000000000..701024cba4 --- /dev/null +++ b/.changeset/pretty-onions-knock.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-azure-devops-backend': patch +'@backstage/plugin-azure-devops-common': patch +'@backstage/plugin-azure-devops': patch +--- + +Introduced new `AzureDevOpsAnnotatorProcessor` that adds the needed annotations automatically. Also, moved constants to common package so they can be shared more easily diff --git a/plugins/azure-devops-backend/README.md b/plugins/azure-devops-backend/README.md index e892b84dd1..6810112bdb 100644 --- a/plugins/azure-devops-backend/README.md +++ b/plugins/azure-devops-backend/README.md @@ -89,6 +89,29 @@ In your `packages/backend/src/index.ts` make the following changes: backend.start(); ``` +## Processor + +The Azure DevOps backend plugin includes the `AzureDevOpsAnnotatorProcessor` which will automatically add the needed annotations for you. Here's how to install it: + +```diff + import { CatalogBuilder } from '@backstage/plugin-catalog-backend'; + import { ScaffolderEntitiesProcessor } from '@backstage/plugin-catalog-backend-module-scaffolder-entity-model'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; ++ import { AzureDevOpsAnnotatorProcessor } from '@backstage/plugin-azure-devops-backend'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + const builder = await CatalogBuilder.create(env); + builder.addProcessor(new ScaffolderEntitiesProcessor()); ++ builder.addProcessor(AzureDevOpsAnnotatorProcessor.fromConfig(env.config)); + const { processingEngine, router } = await builder.build(); + await processingEngine.start(); + return router; + } +``` + ## Links - [Frontend part of the plugin](https://github.com/backstage/backstage/tree/master/plugins/azure-devops) diff --git a/plugins/azure-devops-backend/api-report.md b/plugins/azure-devops-backend/api-report.md index 8c916a1495..82a0875798 100644 --- a/plugins/azure-devops-backend/api-report.md +++ b/plugins/azure-devops-backend/api-report.md @@ -7,21 +7,44 @@ import { BackendFeature } from '@backstage/backend-plugin-api'; import { Build } from 'azure-devops-node-api/interfaces/BuildInterfaces'; import { BuildDefinitionReference } from 'azure-devops-node-api/interfaces/BuildInterfaces'; import { BuildRun } from '@backstage/plugin-azure-devops-common'; +import { CatalogProcessor } from '@backstage/plugin-catalog-node'; import { Config } from '@backstage/config'; import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; +import { Entity } from '@backstage/catalog-model'; import express from 'express'; import { GitRepository } from 'azure-devops-node-api/interfaces/GitInterfaces'; import { GitTag } from '@backstage/plugin-azure-devops-common'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { Logger } from 'winston'; import { Project } from '@backstage/plugin-azure-devops-common'; import { PullRequest } from '@backstage/plugin-azure-devops-common'; import { PullRequestOptions } from '@backstage/plugin-azure-devops-common'; import { RepoBuild } from '@backstage/plugin-azure-devops-common'; +import { ScmIntegrationRegistry } from '@backstage/integration'; import { Team } from '@backstage/plugin-azure-devops-common'; import { TeamMember } from '@backstage/plugin-azure-devops-common'; import { UrlReader } from '@backstage/backend-common'; import { WebApi } from 'azure-devops-node-api'; +// @public (undocumented) +export class AzureDevOpsAnnotatorProcessor implements CatalogProcessor { + constructor(opts: { + scmIntegrationRegistry: ScmIntegrationRegistry; + kinds?: string[]; + }); + // (undocumented) + static fromConfig( + config: Config, + options?: { + kinds?: string[]; + }, + ): AzureDevOpsAnnotatorProcessor; + // (undocumented) + getProcessorName(): string; + // (undocumented) + preProcessEntity(entity: Entity, location: LocationSpec): Promise; +} + // @public (undocumented) export class AzureDevOpsApi { constructor(logger: Logger, webApi: WebApi, urlReader: UrlReader); diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index 5bf6b9b39f..5b8bcda31c 100644 --- a/plugins/azure-devops-backend/package.json +++ b/plugins/azure-devops-backend/package.json @@ -30,12 +30,17 @@ "dependencies": { "@backstage/backend-common": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", + "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", + "@backstage/integration": "workspace:^", "@backstage/plugin-azure-devops-common": "workspace:^", + "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", "@types/express": "^4.17.6", "azure-devops-node-api": "^11.0.1", "express": "^4.17.1", "express-promise-router": "^4.1.0", + "lodash": "^4.17.21", "mime-types": "^2.1.27", "p-limit": "^3.1.0", "winston": "^3.2.1", diff --git a/plugins/azure-devops-backend/src/index.ts b/plugins/azure-devops-backend/src/index.ts index 3c802e7144..61cf7c7a6f 100644 --- a/plugins/azure-devops-backend/src/index.ts +++ b/plugins/azure-devops-backend/src/index.ts @@ -23,3 +23,4 @@ export { AzureDevOpsApi } from './api'; export * from './service/router'; export { azureDevOpsPlugin as default } from './plugin'; +export { AzureDevOpsAnnotatorProcessor } from './processor'; diff --git a/plugins/azure-devops-backend/src/processor/AzureDevOpsAnnotatorProcessor.test.ts b/plugins/azure-devops-backend/src/processor/AzureDevOpsAnnotatorProcessor.test.ts new file mode 100644 index 0000000000..dd83c56f70 --- /dev/null +++ b/plugins/azure-devops-backend/src/processor/AzureDevOpsAnnotatorProcessor.test.ts @@ -0,0 +1,271 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Entity } from '@backstage/catalog-model'; +import { ConfigReader } from '@backstage/config'; +import { AzureDevOpsAnnotatorProcessor } from './AzureDevOpsAnnotatorProcessor'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; + +describe('AzureDevOpsAnnotatorProcessor', () => { + it('adds annotation', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + }, + }; + const location: LocationSpec = { + type: 'url', + target: + 'https://dev.azure.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + }; + + const processor = AzureDevOpsAnnotatorProcessor.fromConfig( + new ConfigReader({}), + ); + + expect(await processor.preProcessEntity(entity, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + annotations: { + 'dev.azure.com/host-org': 'dev.azure.com/organization', + 'dev.azure.com/project-repo': 'project/repository', + }, + }, + }); + }); + + it('adds annotation for Azure DevOps Server url', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + }, + }; + const location: LocationSpec = { + type: 'url', + target: + 'https://example.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + }; + + const processor = AzureDevOpsAnnotatorProcessor.fromConfig( + new ConfigReader({ + integrations: { + azure: [ + { + host: 'example.com', + credentials: [ + { + personalAccessToken: 'pat', + }, + ], + }, + ], + }, + }), + ); + + expect(await processor.preProcessEntity(entity, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + annotations: { + 'dev.azure.com/host-org': 'example.com/organization', + 'dev.azure.com/project-repo': 'project/repository', + }, + }, + }); + }); + + it('adds annotation for TFS subpath url', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + }, + }; + const location: LocationSpec = { + type: 'url', + target: + 'https://example.com/tfs/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + }; + + const processor = AzureDevOpsAnnotatorProcessor.fromConfig( + new ConfigReader({ + integrations: { + azure: [ + { + host: 'example.com', + credentials: [ + { + personalAccessToken: 'pat', + }, + ], + }, + ], + }, + }), + ); + + expect(await processor.preProcessEntity(entity, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + annotations: { + 'dev.azure.com/host-org': 'example.com/tfs/organization', + 'dev.azure.com/project-repo': 'project/repository', + }, + }, + }); + }); + + it('does not override existing annotation', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + annotations: { + 'dev.azure.com/host-org': 'myhost/myorg', + 'dev.azure.com/project-repo': 'myproj/myrepo', + }, + }, + }; + const location: LocationSpec = { + type: 'url', + target: + 'https://dev.azure.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + }; + + const processor = AzureDevOpsAnnotatorProcessor.fromConfig( + new ConfigReader({}), + ); + + expect(await processor.preProcessEntity(entity, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + annotations: { + 'dev.azure.com/host-org': 'myhost/myorg', + 'dev.azure.com/project-repo': 'myproj/myrepo', + }, + }, + }); + }); + + it('should not add annotation for other providers', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + }, + }; + const location: LocationSpec = { + type: 'url', + target: + 'https://not-in-mock-config.example.com/backstage/backstage/-/blob/master/catalog-info.yaml', + }; + + const processor = AzureDevOpsAnnotatorProcessor.fromConfig( + new ConfigReader({}), + ); + + expect(await processor.preProcessEntity(entity, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + }, + }); + }); + + it('should only process applicable kinds', async () => { + const component: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + }, + }; + + const api: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'API', + metadata: { + name: 'my-component', + }, + }; + + const system: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'System', + metadata: { + name: 'my-component', + }, + }; + + const location: LocationSpec = { + type: 'url', + target: + 'https://dev.azure.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + }; + + const processor = AzureDevOpsAnnotatorProcessor.fromConfig( + new ConfigReader({}), + { kinds: ['API', 'Component'] }, + ); + + expect(await processor.preProcessEntity(component, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + annotations: { + 'dev.azure.com/host-org': 'dev.azure.com/organization', + 'dev.azure.com/project-repo': 'project/repository', + }, + }, + }); + + expect(await processor.preProcessEntity(api, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'API', + metadata: { + name: 'my-component', + annotations: { + 'dev.azure.com/host-org': 'dev.azure.com/organization', + 'dev.azure.com/project-repo': 'project/repository', + }, + }, + }); + + expect(await processor.preProcessEntity(system, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'System', + metadata: { + name: 'my-component', + }, + }); + }); +}); diff --git a/plugins/azure-devops-backend/src/processor/AzureDevOpsAnnotatorProcessor.ts b/plugins/azure-devops-backend/src/processor/AzureDevOpsAnnotatorProcessor.ts new file mode 100644 index 0000000000..c27195b8ac --- /dev/null +++ b/plugins/azure-devops-backend/src/processor/AzureDevOpsAnnotatorProcessor.ts @@ -0,0 +1,127 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { Config } from '@backstage/config'; +import { + ScmIntegrationRegistry, + ScmIntegrations, +} from '@backstage/integration'; +import { identity, merge, pickBy } from 'lodash'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; +import { CatalogProcessor } from '@backstage/plugin-catalog-node'; +import { + AZURE_DEVOPS_HOST_ORG_ANNOTATION, + AZURE_DEVOPS_REPO_ANNOTATION, +} from '@backstage/plugin-azure-devops-common'; +import { parseAzureDevOpsUrl } from '../utils'; + +/** @public */ +export class AzureDevOpsAnnotatorProcessor implements CatalogProcessor { + constructor( + private readonly opts: { + scmIntegrationRegistry: ScmIntegrationRegistry; + kinds?: string[]; + }, + ) {} + + getProcessorName(): string { + return 'AzureDevOpsAnnotatorProcessor'; + } + + static fromConfig( + config: Config, + options?: { kinds?: string[] }, + ): AzureDevOpsAnnotatorProcessor { + return new AzureDevOpsAnnotatorProcessor({ + scmIntegrationRegistry: ScmIntegrations.fromConfig(config), + kinds: options?.kinds, + }); + } + + async preProcessEntity( + entity: Entity, + location: LocationSpec, + ): Promise { + const applicableKinds = (this.opts.kinds ?? ['Component']).map(k => + k.toLocaleLowerCase('en-US'), + ); + if ( + !applicableKinds.includes(entity.kind.toLocaleLowerCase('en-US')) || + location.type !== 'url' + ) { + return entity; + } + + const scmIntegration = this.opts.scmIntegrationRegistry.byUrl( + location.target, + ); + + if (!scmIntegration) { + return entity; + } + + if (scmIntegration.type !== 'azure') { + return entity; + } + + const { host, org, project, repo } = parseAzureDevOpsUrl(location.target); + + if (!org || !project || !repo) { + return entity; + } + + const hostOrgAnnotation = AZURE_DEVOPS_HOST_ORG_ANNOTATION; + let hostOrgValue = entity.metadata.annotations?.[hostOrgAnnotation]; + if (!hostOrgValue) { + hostOrgValue = `${host}/${org}`; + } + + const projectRepoAnnotation = AZURE_DEVOPS_REPO_ANNOTATION; + let projectRepoValue = entity.metadata.annotations?.[projectRepoAnnotation]; + if (!projectRepoValue) { + projectRepoValue = `${project}/${repo}`; + } + + const result = merge( + { + metadata: { + annotations: pickBy( + { + [hostOrgAnnotation]: hostOrgValue, + }, + identity, + ), + }, + }, + entity, + ); + + return merge( + { + metadata: { + annotations: pickBy( + { + [projectRepoAnnotation]: projectRepoValue, + }, + identity, + ), + }, + }, + result, + ); + } +} diff --git a/plugins/azure-devops-backend/src/processor/index.ts b/plugins/azure-devops-backend/src/processor/index.ts new file mode 100644 index 0000000000..46747c4802 --- /dev/null +++ b/plugins/azure-devops-backend/src/processor/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { AzureDevOpsAnnotatorProcessor } from './AzureDevOpsAnnotatorProcessor'; diff --git a/plugins/azure-devops-backend/src/utils/azure-devops-utils.test.ts b/plugins/azure-devops-backend/src/utils/azure-devops-utils.test.ts index 427980be14..86a06b7b0b 100644 --- a/plugins/azure-devops-backend/src/utils/azure-devops-utils.test.ts +++ b/plugins/azure-devops-backend/src/utils/azure-devops-utils.test.ts @@ -28,6 +28,7 @@ import { getPullRequestLink, replaceReadme, buildEncodedUrl, + parseAzureDevOpsUrl, } from './azure-devops-utils'; import { GitPullRequest } from 'azure-devops-node-api/interfaces/GitInterfaces'; import { UrlReader } from '@backstage/backend-common'; @@ -295,3 +296,38 @@ describe('buildEncodedUrl', () => { ); }); }); + +describe('parseAzureDevOpsUrl', () => { + it('parses Azure DevOps Cloud url', async () => { + const result = parseAzureDevOpsUrl( + 'https://dev.azure.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + ); + + expect(result.host).toEqual('dev.azure.com'); + expect(result.org).toEqual('organization'); + expect(result.project).toEqual('project'); + expect(result.repo).toEqual('repository'); + }); + + it('parses Azure DevOps Server url', async () => { + const result = parseAzureDevOpsUrl( + 'https://server.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + ); + + expect(result.host).toEqual('server.com'); + expect(result.org).toEqual('organization'); + expect(result.project).toEqual('project'); + expect(result.repo).toEqual('repository'); + }); + + it('parses TFS subpath Url', async () => { + const result = parseAzureDevOpsUrl( + 'https://server.com/tfs/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + ); + + expect(result.host).toEqual('server.com/tfs'); + expect(result.org).toEqual('organization'); + expect(result.project).toEqual('project'); + expect(result.repo).toEqual('repository'); + }); +}); diff --git a/plugins/azure-devops-backend/src/utils/azure-devops-utils.ts b/plugins/azure-devops-backend/src/utils/azure-devops-utils.ts index aa946e7a74..7f5bf39ac4 100644 --- a/plugins/azure-devops-backend/src/utils/azure-devops-utils.ts +++ b/plugins/azure-devops-backend/src/utils/azure-devops-utils.ts @@ -328,3 +328,29 @@ export function extractPartsFromAsset(content: string): { path: path.startsWith('.') ? path.substring(1, path.length) : path, }; } + +export function parseAzureDevOpsUrl(sourceUrl: string) { + const url = new URL(sourceUrl); + + let host = url.host; + let org; + let project; + let repo; + + const parts = url.pathname.split('/').map(part => decodeURIComponent(part)); + if (parts[2] === '_git') { + org = parts[1]; + project = repo = parts[3]; + } else if (parts[3] === '_git') { + org = parts[1]; + project = parts[2]; + repo = parts[4]; + } else if (parts[4] === '_git') { + host = `${host}/${parts[1]}`; + org = parts[2]; + project = parts[3]; + repo = parts[5]; + } + + return { host, org, project, repo }; +} diff --git a/plugins/azure-devops-common/api-report.md b/plugins/azure-devops-common/api-report.md index ea9f7130c0..262d5c8136 100644 --- a/plugins/azure-devops-common/api-report.md +++ b/plugins/azure-devops-common/api-report.md @@ -3,6 +3,22 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +// @public (undocumented) +export const AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION = + 'dev.azure.com/build-definition'; + +// @public (undocumented) +export const AZURE_DEVOPS_DEFAULT_TOP: number; + +// @public (undocumented) +export const AZURE_DEVOPS_HOST_ORG_ANNOTATION = 'dev.azure.com/host-org'; + +// @public (undocumented) +export const AZURE_DEVOPS_PROJECT_ANNOTATION = 'dev.azure.com/project'; + +// @public (undocumented) +export const AZURE_DEVOPS_REPO_ANNOTATION = 'dev.azure.com/project-repo'; + // @public (undocumented) export enum BuildResult { Canceled = 32, diff --git a/plugins/azure-devops/src/constants.ts b/plugins/azure-devops-common/src/constants.ts similarity index 92% rename from plugins/azure-devops/src/constants.ts rename to plugins/azure-devops-common/src/constants.ts index aba0ec093a..5cc6b54592 100644 --- a/plugins/azure-devops/src/constants.ts +++ b/plugins/azure-devops-common/src/constants.ts @@ -14,9 +14,14 @@ * limitations under the License. */ +/** @public */ export const AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION = 'dev.azure.com/build-definition'; +/** @public */ export const AZURE_DEVOPS_HOST_ORG_ANNOTATION = 'dev.azure.com/host-org'; +/** @public */ export const AZURE_DEVOPS_PROJECT_ANNOTATION = 'dev.azure.com/project'; +/** @public */ export const AZURE_DEVOPS_REPO_ANNOTATION = 'dev.azure.com/project-repo'; +/** @public */ export const AZURE_DEVOPS_DEFAULT_TOP: number = 10; diff --git a/plugins/azure-devops-common/src/index.ts b/plugins/azure-devops-common/src/index.ts index 26a854de89..9a4a007317 100644 --- a/plugins/azure-devops-common/src/index.ts +++ b/plugins/azure-devops-common/src/index.ts @@ -15,3 +15,4 @@ */ export * from './types'; +export * from './constants'; diff --git a/plugins/azure-devops/src/hooks/useBuildRuns.ts b/plugins/azure-devops/src/hooks/useBuildRuns.ts index 2b70ea63a5..0c10dc8db8 100644 --- a/plugins/azure-devops/src/hooks/useBuildRuns.ts +++ b/plugins/azure-devops/src/hooks/useBuildRuns.ts @@ -15,11 +15,11 @@ */ import { + AZURE_DEVOPS_DEFAULT_TOP, BuildRun, BuildRunOptions, } from '@backstage/plugin-azure-devops-common'; -import { AZURE_DEVOPS_DEFAULT_TOP } from '../constants'; import { azureDevOpsApiRef } from '../api'; import { useApi } from '@backstage/core-plugin-api'; import useAsync from 'react-use/lib/useAsync'; diff --git a/plugins/azure-devops/src/hooks/usePullRequests.ts b/plugins/azure-devops/src/hooks/usePullRequests.ts index de886919eb..1ac6afae24 100644 --- a/plugins/azure-devops/src/hooks/usePullRequests.ts +++ b/plugins/azure-devops/src/hooks/usePullRequests.ts @@ -15,12 +15,12 @@ */ import { + AZURE_DEVOPS_DEFAULT_TOP, PullRequest, PullRequestOptions, PullRequestStatus, } from '@backstage/plugin-azure-devops-common'; -import { AZURE_DEVOPS_DEFAULT_TOP } from '../constants'; import { Entity } from '@backstage/catalog-model'; import { azureDevOpsApiRef } from '../api'; import { useApi } from '@backstage/core-plugin-api'; diff --git a/plugins/azure-devops/src/plugin.ts b/plugins/azure-devops/src/plugin.ts index 4dd423f35a..1e76c9e53a 100644 --- a/plugins/azure-devops/src/plugin.ts +++ b/plugins/azure-devops/src/plugin.ts @@ -14,11 +14,6 @@ * limitations under the License. */ -import { - AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION, - AZURE_DEVOPS_PROJECT_ANNOTATION, - AZURE_DEVOPS_REPO_ANNOTATION, -} from './constants'; import { azurePipelinesEntityContentRouteRef, azurePullRequestDashboardRouteRef, @@ -37,6 +32,11 @@ import { import { AzureDevOpsClient } from './api/AzureDevOpsClient'; import { Entity } from '@backstage/catalog-model'; import { azureDevOpsApiRef } from './api/AzureDevOpsApi'; +import { + AZURE_DEVOPS_REPO_ANNOTATION, + AZURE_DEVOPS_PROJECT_ANNOTATION, + AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION, +} from '@backstage/plugin-azure-devops-common'; /** @public */ export const isAzureDevOpsAvailable = (entity: Entity) => diff --git a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts index 9feec8bf95..dec140706c 100644 --- a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts +++ b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts @@ -14,14 +14,13 @@ * limitations under the License. */ -import { - AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION, - AZURE_DEVOPS_HOST_ORG_ANNOTATION, - AZURE_DEVOPS_PROJECT_ANNOTATION, - AZURE_DEVOPS_REPO_ANNOTATION, -} from '../constants'; - import { Entity } from '@backstage/catalog-model'; +import { + AZURE_DEVOPS_PROJECT_ANNOTATION, + AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION, + AZURE_DEVOPS_REPO_ANNOTATION, + AZURE_DEVOPS_HOST_ORG_ANNOTATION, +} from '@backstage/plugin-azure-devops-common'; export function getAnnotationValuesFromEntity(entity: Entity): { project: string; diff --git a/yarn.lock b/yarn.lock index 1b42d4915a..7c124c371d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5049,14 +5049,19 @@ __metadata: dependencies: "@backstage/backend-common": "workspace:^" "@backstage/backend-plugin-api": "workspace:^" + "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" + "@backstage/integration": "workspace:^" "@backstage/plugin-azure-devops-common": "workspace:^" + "@backstage/plugin-catalog-common": "workspace:^" + "@backstage/plugin-catalog-node": "workspace:^" "@types/express": ^4.17.6 "@types/supertest": ^2.0.8 azure-devops-node-api: ^11.0.1 express: ^4.17.1 express-promise-router: ^4.1.0 + lodash: ^4.17.21 mime-types: ^2.1.27 msw: ^1.0.0 p-limit: ^3.1.0