fix!: fix event support for Bitbucket Cloud
Fixes the event-based updates at `BitbucketCloudEntityProvider`. Previously, this entity provider had optional event support for legacy backends that could be enabled by passing `catalogApi`, `events`, and `tokenManager`. For the new/current backend system, the `catalogModuleBitbucketCloudEntityProvider` (`catalog.bitbucket-cloud-entity-provider`), event support was enabled by default. A recent change removed `tokenManager` as a dependency from the module as well as removed it as input. While this didn't break the instantiation of the module, it broke the event-based updates and led to a runtime misbehavior, accompanied by an info log message. This change will replace the use of `tokenManager` with the use of `auth` (`AuthService`). Additionally, it will make `catalogApi` and `events` required dependencies. For the current backend system, this change is transparent and doesn't require any action. For the legacy backend system, this change will require you to pass those dependencies if you didn't do it already. **BREAKING CHANGES:** > _(For legacy backend users only.)_ > > Previously optional `catalogApi`, and `events` are required now. > A new required dependency `auth` was added. Fixes: #26925 Relates-to: PR #26141 Signed-off-by: Patrick Jungermann <Patrick.Jungermann@gmail.com>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-bitbucket-cloud': minor
|
||||
---
|
||||
|
||||
Fixes the event-based updates at `BitbucketCloudEntityProvider`.
|
||||
|
||||
Previously, this entity provider had optional event support for legacy backends
|
||||
that could be enabled by passing `catalogApi`, `events`, and `tokenManager`.
|
||||
|
||||
For the new/current backend system, the `catalogModuleBitbucketCloudEntityProvider`
|
||||
(`catalog.bitbucket-cloud-entity-provider`), event support was enabled by default.
|
||||
|
||||
A recent change removed `tokenManager` as a dependency from the module as well as removed it as input.
|
||||
While this didn't break the instantiation of the module, it broke the event-based updates,
|
||||
and led to a runtime misbehavior, accompanied by an info log message.
|
||||
|
||||
This change will replace the use of `tokenManager` with the use of `auth` (`AuthService`).
|
||||
|
||||
Additionally, to simplify, it will make `catalogApi` and `events` required dependencies.
|
||||
For the current backend system, this change is transparent and doesn't require any action.
|
||||
For the legacy backend system, this change will require you to pass those dependencies
|
||||
if you didn't do it already.
|
||||
|
||||
BREAKING CHANGES:
|
||||
|
||||
_(For legacy backend users only.)_
|
||||
|
||||
Previously optional `catalogApi`, and `events` are required now.
|
||||
A new required dependency `auth` was added.
|
||||
@@ -51,33 +51,6 @@ Further documentation:
|
||||
|
||||
### Installation with Legacy Backend System
|
||||
|
||||
#### Installation without Events Support
|
||||
|
||||
And then add the entity provider to your catalog builder:
|
||||
|
||||
```ts title="packages/backend/src/plugins/catalog.ts"
|
||||
/* highlight-add-next-line */
|
||||
import { BitbucketCloudEntityProvider } from '@backstage/plugin-catalog-backend-module-bitbucket-cloud';
|
||||
|
||||
export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
): Promise<Router> {
|
||||
const builder = await CatalogBuilder.create(env);
|
||||
/* highlight-add-start */
|
||||
builder.addEntityProvider(
|
||||
BitbucketCloudEntityProvider.fromConfig(env.config, {
|
||||
logger: env.logger,
|
||||
scheduler: env.scheduler,
|
||||
}),
|
||||
);
|
||||
/* highlight-add-end */
|
||||
|
||||
// ..
|
||||
}
|
||||
```
|
||||
|
||||
#### Installation with Events Support
|
||||
|
||||
Please follow the installation instructions at
|
||||
|
||||
- <https://github.com/backstage/backstage/tree/master/plugins/events-backend/README.md>
|
||||
@@ -104,19 +77,17 @@ export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
): Promise<Router> {
|
||||
const builder = await CatalogBuilder.create(env);
|
||||
builder.addProcessor(new ScaffolderEntitiesProcessor());
|
||||
/* highlight-add-start */
|
||||
const bitbucketCloudProvider = BitbucketCloudEntityProvider.fromConfig(
|
||||
env.config,
|
||||
{
|
||||
auth: env.auth,
|
||||
catalogApi: new CatalogClient({ discoveryApi: env.discovery }),
|
||||
events: env.events,
|
||||
logger: env.logger,
|
||||
scheduler: env.scheduler,
|
||||
tokenManager: env.tokenManager,
|
||||
},
|
||||
);
|
||||
env.eventBroker.subscribe(bitbucketCloudProvider);
|
||||
builder.addEntityProvider(bitbucketCloudProvider);
|
||||
/* highlight-add-end */
|
||||
const { processingEngine, router } = await builder.build();
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.25.0",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/catalog-client": "workspace:^",
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { AuthService } from '@backstage/backend-plugin-api';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { Config } from '@backstage/config';
|
||||
import { EntityProvider } from '@backstage/plugin-catalog-node';
|
||||
@@ -12,7 +13,6 @@ import { EventsService } from '@backstage/plugin-events-node';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { SchedulerService } from '@backstage/backend-plugin-api';
|
||||
import { SchedulerServiceTaskRunner } from '@backstage/backend-plugin-api';
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
|
||||
// @public
|
||||
export class BitbucketCloudEntityProvider implements EntityProvider {
|
||||
@@ -21,12 +21,12 @@ export class BitbucketCloudEntityProvider implements EntityProvider {
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
options: {
|
||||
catalogApi?: CatalogApi;
|
||||
events?: EventsService;
|
||||
auth: AuthService;
|
||||
catalogApi: CatalogApi;
|
||||
events: EventsService;
|
||||
logger: LoggerService;
|
||||
schedule?: SchedulerServiceTaskRunner;
|
||||
scheduler?: SchedulerService;
|
||||
tokenManager?: TokenManager;
|
||||
},
|
||||
): BitbucketCloudEntityProvider[];
|
||||
getProviderName(): string;
|
||||
@@ -39,7 +39,7 @@ export class BitbucketCloudEntityProvider implements EntityProvider {
|
||||
|
||||
// Warnings were encountered during analysis:
|
||||
//
|
||||
// src/providers/BitbucketCloudEntityProvider.d.ts:28:5 - (ae-undocumented) Missing documentation for "fromConfig".
|
||||
// src/providers/BitbucketCloudEntityProvider.d.ts:44:5 - (ae-undocumented) Missing documentation for "refresh".
|
||||
// src/providers/BitbucketCloudEntityProvider.d.ts:47:5 - (ae-undocumented) Missing documentation for "onRepoPush".
|
||||
// src/providers/BitbucketCloudEntityProvider.d.ts:26:5 - (ae-undocumented) Missing documentation for "fromConfig".
|
||||
// src/providers/BitbucketCloudEntityProvider.d.ts:42:5 - (ae-undocumented) Missing documentation for "refresh".
|
||||
// src/providers/BitbucketCloudEntityProvider.d.ts:44:5 - (ae-undocumented) Missing documentation for "onRepoPush".
|
||||
```
|
||||
|
||||
+11
-1
@@ -34,6 +34,7 @@ export const catalogModuleBitbucketCloudEntityProvider = createBackendModule({
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: {
|
||||
auth: coreServices.auth,
|
||||
catalog: catalogProcessingExtensionPoint,
|
||||
catalogApi: catalogServiceRef,
|
||||
config: coreServices.rootConfig,
|
||||
@@ -41,8 +42,17 @@ export const catalogModuleBitbucketCloudEntityProvider = createBackendModule({
|
||||
logger: coreServices.logger,
|
||||
scheduler: coreServices.scheduler,
|
||||
},
|
||||
async init({ catalog, catalogApi, config, events, logger, scheduler }) {
|
||||
async init({
|
||||
auth,
|
||||
catalog,
|
||||
catalogApi,
|
||||
config,
|
||||
events,
|
||||
logger,
|
||||
scheduler,
|
||||
}) {
|
||||
const providers = BitbucketCloudEntityProvider.fromConfig(config, {
|
||||
auth,
|
||||
catalogApi,
|
||||
events,
|
||||
logger,
|
||||
|
||||
+51
-9
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
import {
|
||||
SchedulerServiceTaskInvocationDefinition,
|
||||
SchedulerServiceTaskRunner,
|
||||
@@ -92,11 +91,6 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
applyMutation: jest.fn(),
|
||||
refresh: jest.fn(),
|
||||
};
|
||||
const tokenManager = {
|
||||
getToken: async () => {
|
||||
return { token: 'fake-token' };
|
||||
},
|
||||
} as any as TokenManager;
|
||||
const repoPushEvent: Events.RepoPushEvent = {
|
||||
actor: {
|
||||
type: 'user',
|
||||
@@ -158,8 +152,14 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
});
|
||||
|
||||
it('no provider config', () => {
|
||||
const auth = mockServices.auth.mock();
|
||||
const catalogApi = catalogServiceMock.mock();
|
||||
const config = new ConfigReader({});
|
||||
const events = DefaultEventsService.create({ logger });
|
||||
const providers = BitbucketCloudEntityProvider.fromConfig(config, {
|
||||
auth,
|
||||
catalogApi,
|
||||
events,
|
||||
logger,
|
||||
schedule,
|
||||
});
|
||||
@@ -168,7 +168,13 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
});
|
||||
|
||||
it('single simple provider config', () => {
|
||||
const auth = mockServices.auth.mock();
|
||||
const catalogApi = catalogServiceMock.mock();
|
||||
const events = DefaultEventsService.create({ logger });
|
||||
const providers = BitbucketCloudEntityProvider.fromConfig(simpleConfig, {
|
||||
auth,
|
||||
catalogApi,
|
||||
events,
|
||||
logger,
|
||||
schedule,
|
||||
});
|
||||
@@ -180,14 +186,24 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
});
|
||||
|
||||
it('fail without schedule and scheduler', () => {
|
||||
const auth = mockServices.auth.mock();
|
||||
const catalogApi = catalogServiceMock.mock();
|
||||
const events = DefaultEventsService.create({ logger });
|
||||
|
||||
expect(() =>
|
||||
BitbucketCloudEntityProvider.fromConfig(simpleConfig, {
|
||||
auth,
|
||||
catalogApi,
|
||||
events,
|
||||
logger,
|
||||
}),
|
||||
).toThrow('Either schedule or scheduler must be provided.');
|
||||
});
|
||||
|
||||
it('fail with scheduler but no schedule config', () => {
|
||||
const auth = mockServices.auth.mock();
|
||||
const catalogApi = catalogServiceMock.mock();
|
||||
const events = DefaultEventsService.create({ logger });
|
||||
const scheduler = mockServices.scheduler.mock();
|
||||
const config = new ConfigReader({
|
||||
catalog: {
|
||||
@@ -201,6 +217,9 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
|
||||
expect(() =>
|
||||
BitbucketCloudEntityProvider.fromConfig(config, {
|
||||
auth,
|
||||
catalogApi,
|
||||
events,
|
||||
logger,
|
||||
scheduler,
|
||||
}),
|
||||
@@ -210,6 +229,9 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
});
|
||||
|
||||
it('single simple provider config with schedule in config', () => {
|
||||
const auth = mockServices.auth.mock();
|
||||
const catalogApi = catalogServiceMock.mock();
|
||||
const events = DefaultEventsService.create({ logger });
|
||||
const scheduler = mockServices.scheduler.mock();
|
||||
const config = new ConfigReader({
|
||||
catalog: {
|
||||
@@ -226,6 +248,9 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
});
|
||||
|
||||
const providers = BitbucketCloudEntityProvider.fromConfig(config, {
|
||||
auth,
|
||||
catalogApi,
|
||||
events,
|
||||
logger,
|
||||
scheduler,
|
||||
});
|
||||
@@ -237,6 +262,8 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
});
|
||||
|
||||
it('multiple provider configs', () => {
|
||||
const auth = mockServices.auth.mock();
|
||||
const catalogApi = catalogServiceMock.mock();
|
||||
const config = new ConfigReader({
|
||||
catalog: {
|
||||
providers: {
|
||||
@@ -251,7 +278,11 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
const events = DefaultEventsService.create({ logger });
|
||||
const providers = BitbucketCloudEntityProvider.fromConfig(config, {
|
||||
auth,
|
||||
catalogApi,
|
||||
events,
|
||||
logger,
|
||||
schedule,
|
||||
});
|
||||
@@ -266,7 +297,13 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
});
|
||||
|
||||
it('apply full update on scheduled execution', async () => {
|
||||
const auth = mockServices.auth.mock();
|
||||
const catalogApi = catalogServiceMock.mock();
|
||||
const events = DefaultEventsService.create({ logger });
|
||||
const provider = BitbucketCloudEntityProvider.fromConfig(defaultConfig, {
|
||||
auth,
|
||||
catalogApi,
|
||||
events,
|
||||
logger,
|
||||
schedule,
|
||||
})[0];
|
||||
@@ -436,6 +473,9 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
'added-module/catalog-custom.yaml',
|
||||
);
|
||||
|
||||
const auth = mockServices.auth.mock({
|
||||
getPluginRequestToken: async () => ({ token: 'fake-token' }),
|
||||
});
|
||||
const events = DefaultEventsService.create({ logger });
|
||||
const catalogApi = catalogServiceMock.mock({
|
||||
getEntities: async (
|
||||
@@ -457,11 +497,11 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
},
|
||||
});
|
||||
const provider = BitbucketCloudEntityProvider.fromConfig(defaultConfig, {
|
||||
auth,
|
||||
catalogApi,
|
||||
events,
|
||||
logger,
|
||||
schedule,
|
||||
tokenManager,
|
||||
})[0];
|
||||
|
||||
server.use(
|
||||
@@ -569,14 +609,15 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
});
|
||||
|
||||
it('no onRepoPush update on non-matching workspace slug', async () => {
|
||||
const auth = mockServices.auth.mock();
|
||||
const catalogApi = catalogServiceMock.mock();
|
||||
const events = DefaultEventsService.create({ logger });
|
||||
const provider = BitbucketCloudEntityProvider.fromConfig(defaultConfig, {
|
||||
auth,
|
||||
catalogApi,
|
||||
events,
|
||||
logger,
|
||||
schedule,
|
||||
tokenManager,
|
||||
})[0];
|
||||
|
||||
await provider.connect(entityProviderConnection);
|
||||
@@ -599,14 +640,15 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
});
|
||||
|
||||
it('no onRepoPush update on non-matching repo slug', async () => {
|
||||
const auth = mockServices.auth.mock();
|
||||
const catalogApi = catalogServiceMock.mock();
|
||||
const events = DefaultEventsService.create({ logger });
|
||||
const provider = BitbucketCloudEntityProvider.fromConfig(defaultConfig, {
|
||||
auth,
|
||||
catalogApi,
|
||||
events,
|
||||
logger,
|
||||
schedule,
|
||||
tokenManager,
|
||||
})[0];
|
||||
|
||||
await provider.connect(entityProviderConnection);
|
||||
|
||||
+36
-56
@@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
import {
|
||||
AuthService,
|
||||
LoggerService,
|
||||
SchedulerService,
|
||||
SchedulerServiceTaskRunner,
|
||||
@@ -66,26 +66,25 @@ interface IngestionTarget {
|
||||
* @public
|
||||
*/
|
||||
export class BitbucketCloudEntityProvider implements EntityProvider {
|
||||
private readonly auth: AuthService;
|
||||
private readonly catalogApi: CatalogApi;
|
||||
private readonly client: BitbucketCloudClient;
|
||||
private readonly config: BitbucketCloudEntityProviderConfig;
|
||||
private readonly events: EventsService;
|
||||
private readonly logger: LoggerService;
|
||||
private readonly scheduleFn: () => Promise<void>;
|
||||
private readonly catalogApi?: CatalogApi;
|
||||
private readonly events?: EventsService;
|
||||
private readonly tokenManager?: TokenManager;
|
||||
private connection?: EntityProviderConnection;
|
||||
|
||||
private eventConfigErrorThrown = false;
|
||||
private connection?: EntityProviderConnection;
|
||||
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
options: {
|
||||
catalogApi?: CatalogApi;
|
||||
events?: EventsService;
|
||||
auth: AuthService;
|
||||
catalogApi: CatalogApi;
|
||||
events: EventsService;
|
||||
logger: LoggerService;
|
||||
schedule?: SchedulerServiceTaskRunner;
|
||||
scheduler?: SchedulerService;
|
||||
tokenManager?: TokenManager;
|
||||
},
|
||||
): BitbucketCloudEntityProvider[] {
|
||||
const integrations = ScmIntegrations.fromConfig(config);
|
||||
@@ -112,35 +111,35 @@ export class BitbucketCloudEntityProvider implements EntityProvider {
|
||||
options.scheduler!.createScheduledTaskRunner(providerConfig.schedule!);
|
||||
|
||||
return new BitbucketCloudEntityProvider(
|
||||
options.auth,
|
||||
options.catalogApi,
|
||||
providerConfig,
|
||||
options.events,
|
||||
integration,
|
||||
options.logger,
|
||||
taskRunner,
|
||||
options.catalogApi,
|
||||
options.events,
|
||||
options.tokenManager,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private constructor(
|
||||
auth: AuthService,
|
||||
catalogApi: CatalogApi,
|
||||
config: BitbucketCloudEntityProviderConfig,
|
||||
events: EventsService,
|
||||
integration: BitbucketCloudIntegration,
|
||||
logger: LoggerService,
|
||||
taskRunner: SchedulerServiceTaskRunner,
|
||||
catalogApi?: CatalogApi,
|
||||
events?: EventsService,
|
||||
tokenManager?: TokenManager,
|
||||
) {
|
||||
this.auth = auth;
|
||||
this.catalogApi = catalogApi;
|
||||
this.client = BitbucketCloudClient.fromConfig(integration.config);
|
||||
this.config = config;
|
||||
this.events = events;
|
||||
this.logger = logger.child({
|
||||
target: this.getProviderName(),
|
||||
});
|
||||
this.scheduleFn = this.createScheduleFn(taskRunner);
|
||||
this.catalogApi = catalogApi;
|
||||
this.events = events;
|
||||
this.tokenManager = tokenManager;
|
||||
}
|
||||
|
||||
private createScheduleFn(
|
||||
@@ -185,19 +184,17 @@ export class BitbucketCloudEntityProvider implements EntityProvider {
|
||||
this.connection = connection;
|
||||
await this.scheduleFn();
|
||||
|
||||
if (this.events) {
|
||||
await this.events.subscribe({
|
||||
id: this.getProviderName(),
|
||||
topics: [TOPIC_REPO_PUSH],
|
||||
onEvent: async params => {
|
||||
if (params.topic !== TOPIC_REPO_PUSH) {
|
||||
return;
|
||||
}
|
||||
await this.events.subscribe({
|
||||
id: this.getProviderName(),
|
||||
topics: [TOPIC_REPO_PUSH],
|
||||
onEvent: async params => {
|
||||
if (params.topic !== TOPIC_REPO_PUSH) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.onRepoPush(params.eventPayload as Events.RepoPushEvent);
|
||||
},
|
||||
});
|
||||
}
|
||||
await this.onRepoPush(params.eventPayload as Events.RepoPushEvent);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async refresh(logger: LoggerService) {
|
||||
@@ -220,32 +217,12 @@ export class BitbucketCloudEntityProvider implements EntityProvider {
|
||||
);
|
||||
}
|
||||
|
||||
private canHandleEvents(): boolean {
|
||||
if (this.catalogApi && this.tokenManager) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// throw only once
|
||||
if (!this.eventConfigErrorThrown) {
|
||||
this.eventConfigErrorThrown = true;
|
||||
throw new Error(
|
||||
`${this.getProviderName()} not well configured to handle repo:push. Missing CatalogApi and/or TokenManager.`,
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private enhanceEvent(event: Events.RepoPushEvent): void {
|
||||
// add missing slug
|
||||
event.repository.slug = event.repository.full_name!.split('/', 2)[1];
|
||||
}
|
||||
|
||||
async onRepoPush(event: Events.RepoPushEvent): Promise<void> {
|
||||
if (!this.canHandleEvents()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.connection) {
|
||||
throw new Error('Not initialized');
|
||||
}
|
||||
@@ -273,8 +250,7 @@ export class BitbucketCloudEntityProvider implements EntityProvider {
|
||||
|
||||
const targets = await this.findCatalogFiles(repoSlug);
|
||||
|
||||
const { token } = await this.tokenManager!.getToken();
|
||||
const existing = await this.findExistingLocations(repoUrl, token);
|
||||
const existing = await this.findExistingLocations(repoUrl);
|
||||
|
||||
const added: DeferredEntity[] = this.toDeferredEntities(
|
||||
targets.filter(
|
||||
@@ -321,16 +297,20 @@ export class BitbucketCloudEntityProvider implements EntityProvider {
|
||||
|
||||
private async findExistingLocations(
|
||||
repoUrl: string,
|
||||
token: string,
|
||||
): Promise<LocationEntity[]> {
|
||||
const filter: Record<string, string> = {};
|
||||
filter.kind = 'Location';
|
||||
filter[`metadata.annotations.${ANNOTATION_BITBUCKET_CLOUD_REPO_URL}`] =
|
||||
repoUrl;
|
||||
|
||||
return this.catalogApi!.getEntities({ filter }, { token }).then(
|
||||
result => result.items,
|
||||
) as Promise<LocationEntity[]>;
|
||||
const { token } = await this.auth.getPluginRequestToken({
|
||||
onBehalfOf: await this.auth.getOwnServiceCredentials(),
|
||||
targetPluginId: 'catalog',
|
||||
});
|
||||
|
||||
return this.catalogApi
|
||||
.getEntities({ filter }, { token })
|
||||
.then(result => result.items) as Promise<LocationEntity[]>;
|
||||
}
|
||||
|
||||
private async findCatalogFiles(
|
||||
|
||||
@@ -5581,7 +5581,6 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-catalog-backend-module-bitbucket-cloud@workspace:plugins/catalog-backend-module-bitbucket-cloud"
|
||||
dependencies:
|
||||
"@backstage/backend-common": ^0.25.0
|
||||
"@backstage/backend-plugin-api": "workspace:^"
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
"@backstage/catalog-client": "workspace:^"
|
||||
|
||||
Reference in New Issue
Block a user