diff --git a/.changeset/sharp-ravens-shop.md b/.changeset/sharp-ravens-shop.md new file mode 100644 index 0000000000..eb7486c490 --- /dev/null +++ b/.changeset/sharp-ravens-shop.md @@ -0,0 +1,10 @@ +--- +'@backstage/integration': major +--- + +**BREAKING** Removed deprecated Azure DevOps, Bitbucket, Gerrit and GitHub code: + +- For Azure Devops, the long deprecated `token` string and `credential` object have been removed from the `config.d.ts`. Use the `credentials` array object instead. +- For Bitbucket, the long deprecated `bitbucket` object has been removed from the `config.d.ts`. Use the `bitbucketCloud` or `bitbucketServer` objects instead. +- For Gerrit, the `parseGerritGitilesUrl` function has been removed, use `parseGitilesUrlRef` instead. The `buildGerritGitilesArchiveUrl` function has also been removed, use `buildGerritGitilesArchiveUrlFromLocation` instead. +- For GitHub, the `getGitHubRequestOptions` function has been removed. diff --git a/.changeset/six-trees-carry.md b/.changeset/six-trees-carry.md new file mode 100644 index 0000000000..9e21487c23 --- /dev/null +++ b/.changeset/six-trees-carry.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': minor +--- + +**BREAKING** Removed deprecated `BitbucketUrlReader`. Use the `BitbucketCloudUrlReader` or the `BitbucketServerUrlReader` instead. diff --git a/.changeset/tiny-zoos-smash.md b/.changeset/tiny-zoos-smash.md new file mode 100644 index 0000000000..883ea4f29a --- /dev/null +++ b/.changeset/tiny-zoos-smash.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-common': major +--- + +**BREAKING** Removed deprecated `bitbucket` integration from being registered in the `ScaffolderClient`. Use the `bitbucketCloud` or `bitbucketServer` integrations instead. diff --git a/docs/backend-system/building-backends/08-migrating.md b/docs/backend-system/building-backends/08-migrating.md index 3aa06216b4..fdbac8e65f 100644 --- a/docs/backend-system/building-backends/08-migrating.md +++ b/docs/backend-system/building-backends/08-migrating.md @@ -1340,105 +1340,3 @@ const backend = createBackend(); backend.add(import('@backstage/plugin-kubernetes-backend')); /* highlight-add-end */ ``` - -### The Plugins in Backstage Repo - -The vast majority of the backend plugins that currently live in the Backstage Repo have been migrated and their respective `README`s have details on how they should be installed using the New Backend System. - -| Package | Role | Migrated | Uses Alpha Export | Link to `README` | -| ------------------------------------------------------------------ | --------------------- | -------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -| @backstage-community/plugin-adr-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/adr/plugins/adr-backend/README.md) | -| @backstage-community/plugin-airbrake-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/airbrake/plugins/airbrake-backend/README.md) | -| @backstage/plugin-app-backend | backend-plugin | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/app-backend/README.md) | -| @backstage/plugin-auth-backend | backend-plugin | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend/README.md) | -| @backstage/plugin-auth-backend-module-atlassian-provider | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-atlassian-provider/README.md) | -| @backstage/plugin-auth-backend-module-aws-alb-provider | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-aws-alb-provider/README.md) | -| @backstage/plugin-auth-backend-module-gcp-iap-provider | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-gcp-iap-provider/README.md) | -| @backstage/plugin-auth-backend-module-github-provider | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-github-provider/README.md) | -| @backstage/plugin-auth-backend-module-gitlab-provider | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-gitlab-provider/README.md) | -| @backstage/plugin-auth-backend-module-google-provider | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-google-provider/README.md) | -| @backstage/plugin-auth-backend-module-guest-provider | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-guest-provider/README.md) | -| @backstage/plugin-auth-backend-module-microsoft-provider | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-microsoft-provider/README.md) | -| @backstage/plugin-auth-backend-module-oauth2-provider | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-oauth2-provider/README.md) | -| @backstage/plugin-auth-backend-module-oauth2-proxy-provider | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-oauth2-proxy-provider/README.md) | -| @backstage/plugin-auth-backend-module-oidc-provider | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-oidc-provider/README.md) | -| @backstage/plugin-auth-backend-module-okta-provider | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-okta-provider/README.md) | -| @backstage/plugin-auth-backend-module-pinniped-provider | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-pinniped-provider/README.md) | -| @backstage/plugin-auth-backend-module-vmware-cloud-provider | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-vmware-cloud-provider/README.md) | -| @backstage-community/plugin-azure-devops-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/azure-devops/plugins/azure-devops-backend/README.md) | -| @backstage-community/plugin-azure-sites-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/azure-sites/plugins/azure-sites-backend/README.md) | -| @backstage-community/plugin-badges-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/badges/plugins/badges-backend/README.md) | -| @backstage-community/plugin-bazaar-backend | backend-plugin | true | true | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/bazaar/plugins/bazaar-backend/README.md) | -| @backstage/plugin-catalog-backend | backend-plugin | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/README.md) | -| @backstage/plugin-catalog-backend-module-aws | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-aws/README.md) | -| @backstage/plugin-catalog-backend-module-azure | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-azure/README.md) | -| @backstage/plugin-catalog-backend-module-backstage-openapi | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-backstage-openapi/README.md) | -| @backstage/plugin-catalog-backend-module-bitbucket-cloud | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-bitbucket-cloud/README.md) | -| @backstage/plugin-catalog-backend-module-bitbucket-server | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-bitbucket-server/README.md) | -| @backstage/plugin-catalog-backend-module-gcp | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-gcp/README.md) | -| @backstage/plugin-catalog-backend-module-gerrit | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-gerrit/README.md) | -| @backstage/plugin-catalog-backend-module-github | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-github/README.md) | -| @backstage/plugin-catalog-backend-module-github-org | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-github-org/README.md) | -| @backstage/plugin-catalog-backend-module-gitlab | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-gitlab/README.md) | -| @backstage/plugin-catalog-backend-module-incremental-ingestion | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-incremental-ingestion/README.md) | -| @backstage/plugin-catalog-backend-module-ldap | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-ldap/README.md) | -| @backstage/plugin-catalog-backend-module-msgraph | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-msgraph/README.md) | -| @backstage/plugin-catalog-backend-module-openapi | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-openapi/README.md) | -| @backstage/plugin-catalog-backend-module-puppetdb | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-puppetdb/README.md) | -| @backstage/plugin-catalog-backend-module-scaffolder-entity-model | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-scaffolder-entity-model/README.md) | -| @backstage/plugin-catalog-backend-module-unprocessed | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-unprocessed/README.md) | -| @backstage-community/plugin-code-coverage-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/code-coverage/plugins/code-coverage-backend/README.md) | -| @backstage/plugin-devtools-backend | backend-plugin | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/devtools-backend/README.md) | -| @backstage-community/plugin-entity-feedback-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/entity-feedback/plugins/entity-feedback-backend/README.md) | -| @backstage/plugin-events-backend | backend-plugin | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/events-backend/README.md) | -| @backstage/plugin-events-backend-module-aws-sqs | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/events-backend-module-aws-sqs/README.md) | -| @backstage/plugin-events-backend-module-azure | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/events-backend-module-azure/README.md) | -| @backstage/plugin-events-backend-module-bitbucket-cloud | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/events-backend-module-bitbucket-cloud/README.md) | -| @backstage/plugin-events-backend-module-gerrit | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/events-backend-module-gerrit/README.md) | -| @backstage/plugin-events-backend-module-github | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/events-backend-module-github/README.md) | -| @backstage/plugin-events-backend-module-gitlab | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/events-backend-module-gitlab/README.md) | -| @internal/plugin-todo-list-backend | backend-plugin | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/example-todo-list-backend/README.md) | -| @backstage-community/plugin-explore-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/explore/plugins/explore-backend/README.md) | -| @backstage-community/plugin-jenkins-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/jenkins/plugins/jenkins-backend/README.md) | -| @backstage-community/plugin-kafka-backend | backend-plugin | true | true | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/kafka/plugins/kafka-backend/README.md) | -| @backstage/plugin-kubernetes-backend | backend-plugin | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/kubernetes-backend/README.md) | -| @backstage-community/plugin-lighthouse-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/lighthouse/plugins/lighthouse-backend/README.md) | -| @backstage-community/plugin-linguist-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/linguist/plugins/linguist-backend/README.md) | -| @backstage-community/plugin-nomad-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/nomad/plugins/nomad-backend/README.md) | -| @backstage/plugin-notifications-backend | backend-plugin | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/notifications-backend/README.md) | -| @backstage-community/plugin-periskop-backend | backend-plugin | true | true | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/periskop/plugins/periskop-backend/README.md) | -| @backstage/plugin-permission-backend | backend-plugin | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/permission-backend/README.md) | -| @backstage/plugin-permission-backend-module-allow-all-policy | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/permission-backend-module-policy-allow-all/README.md) | -| @backstage-community/plugin-playlist-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/playlist/plugins/playlist-backend/README.md) | -| @backstage/plugin-proxy-backend | backend-plugin | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/proxy-backend/README.md) | -| @backstage-community/plugin-rollbar-backend | backend-plugin | | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/rollbar/plugins/rollbar-backend/README.md) | -| @backstage/plugin-scaffolder-backend | backend-plugin | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/README.md) | -| @backstage/plugin-scaffolder-backend-module-azure | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-azure/README.md) | -| @backstage/plugin-scaffolder-backend-module-bitbucket | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-bitbucket/README.md) | -| @backstage/plugin-scaffolder-backend-module-bitbucket-cloud | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-bitbucket-cloud/README.md) | -| @backstage/plugin-scaffolder-backend-module-bitbucket-server | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-bitbucket-server/README.md) | -| @backstage/plugin-scaffolder-backend-module-confluence-to-markdown | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-confluence-to-markdown/README.md) | -| @backstage/plugin-scaffolder-backend-module-cookiecutter | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-cookiecutter/README.md) | -| @backstage/plugin-scaffolder-backend-module-gerrit | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-gerrit/README.md) | -| @backstage/plugin-scaffolder-backend-module-gitea | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-gitea/README.md) | -| @backstage/plugin-scaffolder-backend-module-github | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-github/README.md) | -| @backstage/plugin-scaffolder-backend-module-gitlab | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-gitlab/README.md) | -| @backstage/plugin-scaffolder-backend-module-rails | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-rails/README.md) | -| @backstage/plugin-scaffolder-backend-module-sentry | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-sentry/README.md) | -| @backstage/plugin-scaffolder-backend-module-yeoman | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-yeoman/README.md) | -| @backstage/plugin-search-backend | backend-plugin | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/search-backend/README.md) | -| @backstage/plugin-search-backend-module-catalog | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/search-backend-module-catalog/README.md) | -| @backstage/plugin-search-backend-module-elasticsearch | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/search-backend-module-elasticsearch/README.md) | -| @backstage/plugin-search-backend-module-explore | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/search-backend-module-explore/README.md) | -| @backstage/plugin-search-backend-module-pg | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/search-backend-module-pg/README.md) | -| @backstage/plugin-search-backend-module-stack-overflow-collator | backend-plugin-module | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/search-backend-module-stack-overflow-collator/README.md) | -| @backstage/plugin-search-backend-module-techdocs | backend-plugin-module | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/search-backend-module-techdocs/README.md) | -| @backstage/plugin-signals-backend | backend-plugin | true | | [README](https://github.com/backstage/backstage/blob/master/plugins/signals-backend/README.md) | -| @backstage-community/plugin-sonarqube-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/sonarqube/plugins/sonarqube-backend/README.md) | -| @backstage-community/plugin-stack-overflow-backend | backend-plugin | | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/stack-overflow/plugins/stack-overflow-backend/README.md) | -| @backstage-community/plugin-tech-insights-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/tech-insights/plugins/tech-insights-backend/README.md) | -| @backstage-community/plugin-tech-insights-backend-module-jsonfc | backend-plugin-module | true | | [README](https://github.com/backstage/community-plugins/blob/main/workspaces/tech-insights/plugins/tech-insights-backend-module-jsonfc/README.md) | -| @backstage/plugin-techdocs-backend | backend-plugin | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/README.md) | -| @backstage-community/plugin-todo-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/todo/plugins/todo-backend/README.md) | -| @backstage/plugin-user-settings-backend | backend-plugin | true | true | [README](https://github.com/backstage/backstage/blob/master/plugins/user-settings-backend/README.md) | -| @backstage-community/plugin-vault-backend | backend-plugin | true | | [README](https://github.com/backstage/community-plugins/blob/master/workspaces/vault/plugins/vault-backend/README.md) | diff --git a/packages/backend-defaults/report-urlReader.api.md b/packages/backend-defaults/report-urlReader.api.md index 78a35911b8..c32a63231b 100644 --- a/packages/backend-defaults/report-urlReader.api.md +++ b/packages/backend-defaults/report-urlReader.api.md @@ -10,7 +10,6 @@ import { AzureCredentialsManager } from '@backstage/integration'; import { AzureDevOpsCredentialsProvider } from '@backstage/integration'; import { AzureIntegration } from '@backstage/integration'; import { BitbucketCloudIntegration } from '@backstage/integration'; -import { BitbucketIntegration } from '@backstage/integration'; import { BitbucketServerIntegration } from '@backstage/integration'; import { Config } from '@backstage/config'; import { GerritIntegration } from '@backstage/integration'; @@ -190,38 +189,6 @@ export class BitbucketServerUrlReader implements UrlReaderService { toString(): string; } -// @public @deprecated -export class BitbucketUrlReader implements UrlReaderService { - constructor( - integration: BitbucketIntegration, - logger: LoggerService, - deps: { - treeResponseFactory: ReadTreeResponseFactory; - }, - ); - // (undocumented) - static factory: ReaderFactory; - // (undocumented) - read(url: string): Promise; - // (undocumented) - readTree( - url: string, - options?: UrlReaderServiceReadTreeOptions, - ): Promise; - // (undocumented) - readUrl( - url: string, - options?: UrlReaderServiceReadUrlOptions, - ): Promise; - // (undocumented) - search( - url: string, - options?: UrlReaderServiceSearchOptions, - ): Promise; - // (undocumented) - toString(): string; -} - // @public export class FetchUrlReader implements UrlReaderService { static factory: ReaderFactory; diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketUrlReader.test.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketUrlReader.test.ts deleted file mode 100644 index 1a72d2af73..0000000000 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketUrlReader.test.ts +++ /dev/null @@ -1,656 +0,0 @@ -/* - * Copyright 2020 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 { ConfigReader } from '@backstage/config'; -import { - BitbucketIntegration, - readBitbucketIntegrationConfig, -} from '@backstage/integration'; -import { - createMockDirectory, - mockServices, - registerMswTestHooks, -} from '@backstage/backend-test-utils'; -import fs from 'fs-extra'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; -import path from 'node:path'; -import { NotModifiedError } from '@backstage/errors'; -import { BitbucketUrlReader } from './BitbucketUrlReader'; -import { DefaultReadTreeResponseFactory } from './tree'; -import getRawBody from 'raw-body'; -import { UrlReaderServiceReadUrlResponse } from '@backstage/backend-plugin-api'; - -const logger = mockServices.logger.mock(); - -describe('BitbucketUrlReader.factory', () => { - it('only apply integration configs not inherited from bitbucketCloud or bitbucketServer', () => { - const config = new ConfigReader({ - integrations: { - bitbucket: [], - bitbucketCloud: [ - { - username: 'username', - appPassword: 'password', - }, - ], - bitbucketServer: [ - { - host: 'bitbucket-server.local', - token: 'test-token', - }, - ], - }, - }); - const treeResponseFactory = DefaultReadTreeResponseFactory.create({ - config: config, - }); - - const tuples = BitbucketUrlReader.factory({ - config, - logger, - treeResponseFactory, - }); - - expect(tuples).toHaveLength(0); - }); -}); - -describe('BitbucketUrlReader', () => { - const mockDir = createMockDirectory({ mockOsTmpDir: true }); - - beforeEach(mockDir.clear); - - const treeResponseFactory = DefaultReadTreeResponseFactory.create({ - config: new ConfigReader({}), - }); - - const bitbucketProcessor = new BitbucketUrlReader( - new BitbucketIntegration( - readBitbucketIntegrationConfig( - new ConfigReader({ - host: 'bitbucket.org', - apiBaseUrl: 'https://api.bitbucket.org/2.0', - }), - ), - ), - logger, - { treeResponseFactory }, - ); - - const hostedBitbucketProcessor = new BitbucketUrlReader( - new BitbucketIntegration( - readBitbucketIntegrationConfig( - new ConfigReader({ - host: 'bitbucket.mycompany.net', - apiBaseUrl: 'https://api.bitbucket.mycompany.net/rest/api/1.0', - }), - ), - ), - logger, - { treeResponseFactory }, - ); - - const worker = setupServer(); - registerMswTestHooks(worker); - - describe('readUrl', () => { - it('should be able to readUrl via buffer without ETag', async () => { - worker.use( - rest.get( - 'https://api.bitbucket.org/2.0/repositories/backstage-verification/test-template/src/master/template.yaml', - (req, res, ctx) => { - expect(req.headers.get('If-None-Match')).toBeNull(); - return res( - ctx.status(200), - ctx.body('foo'), - ctx.set('ETag', 'etag-value'), - ); - }, - ), - ); - - const result = await bitbucketProcessor.readUrl( - 'https://bitbucket.org/backstage-verification/test-template/src/master/template.yaml', - ); - const buffer = await result.buffer(); - expect(buffer.toString()).toBe('foo'); - }); - - it('should be able to readUrl using provided token', async () => { - worker.use( - rest.get( - 'https://api.bitbucket.org/2.0/repositories/backstage-verification/test-template/src/master/template.yaml', - (req, res, ctx) => { - expect(req.headers.get('authorization')).toBe( - 'Bearer manual-token', - ); - return res(ctx.status(200), ctx.body('foo')); - }, - ), - ); - - const result = await bitbucketProcessor.readUrl( - 'https://bitbucket.org/backstage-verification/test-template/src/master/template.yaml', - { token: 'manual-token' }, - ); - const buffer = await result.buffer(); - expect(buffer.toString()).toBe('foo'); - }); - - it('should be able to readUrl via stream without ETag', async () => { - worker.use( - rest.get( - 'https://api.bitbucket.org/2.0/repositories/backstage-verification/test-template/src/master/template.yaml', - (req, res, ctx) => { - expect(req.headers.get('If-None-Match')).toBeNull(); - return res( - ctx.status(200), - ctx.body('foo'), - ctx.set('ETag', 'etag-value'), - ); - }, - ), - ); - - const result = await bitbucketProcessor.readUrl( - 'https://bitbucket.org/backstage-verification/test-template/src/master/template.yaml', - ); - const fromStream = await getRawBody(result.stream!()); - expect(fromStream.toString()).toBe('foo'); - }); - - it('should be able to readUrl with matching ETag', async () => { - worker.use( - rest.get( - 'https://api.bitbucket.org/2.0/repositories/backstage-verification/test-template/src/master/template.yaml', - (req, res, ctx) => { - expect(req.headers.get('If-None-Match')).toBe( - 'matching-etag-value', - ); - return res(ctx.status(304)); - }, - ), - ); - - await expect( - bitbucketProcessor.readUrl( - 'https://bitbucket.org/backstage-verification/test-template/src/master/template.yaml', - { etag: 'matching-etag-value' }, - ), - ).rejects.toThrow(NotModifiedError); - }); - - it('should be able to readUrl without matching ETag', async () => { - worker.use( - rest.get( - 'https://api.bitbucket.org/2.0/repositories/backstage-verification/test-template/src/master/template.yaml', - (req, res, ctx) => { - expect(req.headers.get('If-None-Match')).toBe( - 'previous-etag-value', - ); - return res( - ctx.status(200), - ctx.body('foo'), - ctx.set('ETag', 'new-etag-value'), - ); - }, - ), - ); - - const result = await bitbucketProcessor.readUrl( - 'https://bitbucket.org/backstage-verification/test-template/src/master/template.yaml', - { etag: 'previous-etag-value' }, - ); - const buffer = await result.buffer(); - expect(buffer.toString()).toBe('foo'); - expect(result.etag).toBe('new-etag-value'); - }); - - it('should be able to readUrl via buffer without If-Modified-Since', async () => { - worker.use( - rest.get( - 'https://api.bitbucket.org/2.0/repositories/backstage-verification/test-template/src/master/template.yaml', - (req, res, ctx) => { - expect(req.headers.get('If-None-Match')).toBeNull(); - return res( - ctx.status(200), - ctx.body('foo'), - ctx.set('ETag', 'etag-value'), - ctx.set( - 'Last-Modified', - new Date('2020-01-01T00:00:00Z').toUTCString(), - ), - ); - }, - ), - ); - - const result = await bitbucketProcessor.readUrl( - 'https://bitbucket.org/backstage-verification/test-template/src/master/template.yaml', - ); - const buffer = await result.buffer(); - expect(result.lastModifiedAt).toEqual(new Date('2020-01-01T00:00:00Z')); - expect(buffer.toString()).toBe('foo'); - }); - - it('should be throw not modified when If-Modified-Since returns a 304', async () => { - worker.use( - rest.get( - 'https://api.bitbucket.org/2.0/repositories/backstage-verification/test-template/src/master/template.yaml', - (req, res, ctx) => { - expect(req.headers.get('If-Modified-Since')).toBe( - new Date('1999 12 31 23:59:59 GMT').toUTCString(), - ); - return res(ctx.status(304)); - }, - ), - ); - - await expect( - bitbucketProcessor.readUrl( - 'https://bitbucket.org/backstage-verification/test-template/src/master/template.yaml', - { lastModifiedAfter: new Date('1999 12 31 23:59:59 GMT') }, - ), - ).rejects.toThrow(NotModifiedError); - }); - - it('should be able to readUrl when If-Modified-Since is before Last-Modified', async () => { - worker.use( - rest.get( - 'https://api.bitbucket.org/2.0/repositories/backstage-verification/test-template/src/master/template.yaml', - (req, res, ctx) => { - expect(req.headers.get('If-Modified-Since')).toBe( - new Date('1999 12 31 23:59:59 GMT').toUTCString(), - ); - return res( - ctx.status(200), - ctx.set( - 'Last-Modified', - new Date('2020-01-01T00:00:00Z').toUTCString(), - ), - ctx.body('foo'), - ); - }, - ), - ); - - const result = await bitbucketProcessor.readUrl( - 'https://bitbucket.org/backstage-verification/test-template/src/master/template.yaml', - { lastModifiedAfter: new Date('1999 12 31 23:59:59 GMT') }, - ); - const buffer = await result.buffer(); - expect(buffer.toString()).toBe('foo'); - expect(result.lastModifiedAt).toEqual(new Date('2020-01-01T00:00:00Z')); - }); - }); - - describe('read', () => { - it('rejects unknown targets', async () => { - await expect( - bitbucketProcessor.read('https://not.bitbucket.com/apa'), - ).rejects.toThrow( - 'Incorrect URL: https://not.bitbucket.com/apa, Error: Invalid Bitbucket URL or file path', - ); - }); - }); - - describe('readTree', () => { - const repoBuffer = fs.readFileSync( - path.resolve( - __dirname, - '__fixtures__/bitbucket-repo-with-commit-hash.tar.gz', - ), - ); - - const privateBitbucketRepoBuffer = fs.readFileSync( - path.resolve(__dirname, '__fixtures__/bitbucket-server-repo.tar.gz'), - ); - - beforeEach(() => { - worker.use( - rest.get( - 'https://api.bitbucket.org/2.0/repositories/backstage/mock', - (_, res, ctx) => - res( - ctx.status(200), - ctx.json({ - mainbranch: { - type: 'branch', - name: 'master', - }, - }), - ), - ), - rest.get( - 'https://bitbucket.org/backstage/mock/get/master.tar.gz', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/zip'), - ctx.set( - 'content-disposition', - 'attachment; filename=backstage-mock-12ab34cd56ef.tar.gz', - ), - ctx.body(new Uint8Array(repoBuffer)), - ), - ), - rest.get( - 'https://api.bitbucket.org/2.0/repositories/backstage/mock/commits/master', - (_, res, ctx) => - res( - ctx.status(200), - ctx.json({ - values: [{ hash: '12ab34cd56ef78gh90ij12kl34mn56op78qr90st' }], - }), - ), - ), - rest.get( - 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/archive', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/zip'), - ctx.set( - 'content-disposition', - 'attachment; filename=backstage-mock.tgz', - ), - ctx.body(new Uint8Array(privateBitbucketRepoBuffer)), - ), - ), - rest.get( - 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/commits', - (_, res, ctx) => - res( - ctx.status(200), - ctx.json({ - values: [{ id: '12ab34cd56ef78gh90ij12kl34mn56op78qr90st' }], - }), - ), - ), - ); - }); - - it('returns the wanted files from an archive', async () => { - const response = await bitbucketProcessor.readTree( - 'https://bitbucket.org/backstage/mock/src/master', - ); - - expect(response.etag).toBe('12ab34cd56ef'); - - const files = await response.files(); - - expect(files.length).toBe(2); - const mkDocsFile = await files[0].content(); - const indexMarkdownFile = await files[1].content(); - - expect(indexMarkdownFile.toString()).toBe('# Test\n'); - expect(mkDocsFile.toString()).toBe('site_name: Test\n'); - }); - - it('creates a directory with the wanted files', async () => { - const response = await bitbucketProcessor.readTree( - 'https://bitbucket.org/backstage/mock', - ); - - const dir = await response.dir({ targetDir: mockDir.path }); - - await expect( - fs.readFile(path.join(dir, 'mkdocs.yml'), 'utf8'), - ).resolves.toBe('site_name: Test\n'); - await expect( - fs.readFile(path.join(dir, 'docs', 'index.md'), 'utf8'), - ).resolves.toBe('# Test\n'); - }); - - it('uses private bitbucket host', async () => { - const response = await hostedBitbucketProcessor.readTree( - 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/docs?at=some-branch', - ); - - expect(response.etag).toBe('12ab34cd56ef'); - - const files = await response.files(); - - expect(files.length).toBe(1); - const indexMarkdownFile = await files[0].content(); - - expect(indexMarkdownFile.toString()).toBe('# Test\n'); - }); - - it('returns the wanted files from an archive with a subpath', async () => { - const response = await bitbucketProcessor.readTree( - 'https://bitbucket.org/backstage/mock/src/master/docs', - ); - - expect(response.etag).toBe('12ab34cd56ef'); - - const files = await response.files(); - - expect(files.length).toBe(1); - const indexMarkdownFile = await files[0].content(); - - expect(indexMarkdownFile.toString()).toBe('# Test\n'); - }); - - it('creates a directory with the wanted files with a subpath', async () => { - const response = await bitbucketProcessor.readTree( - 'https://bitbucket.org/backstage/mock/src/master/docs', - ); - - const dir = await response.dir({ targetDir: mockDir.path }); - - await expect( - fs.readFile(path.join(dir, 'index.md'), 'utf8'), - ).resolves.toBe('# Test\n'); - }); - - it('throws a NotModifiedError when given a etag in options', async () => { - const fnBitbucket = async () => { - await bitbucketProcessor.readTree( - 'https://bitbucket.org/backstage/mock', - { etag: '12ab34cd56ef' }, - ); - }; - - await expect(fnBitbucket).rejects.toThrow(NotModifiedError); - }); - - it('should not throw a NotModifiedError when given an outdated etag in options', async () => { - const response = await bitbucketProcessor.readTree( - 'https://bitbucket.org/backstage/mock', - { etag: 'outdatedetag123abc' }, - ); - - expect(response.etag).toBe('12ab34cd56ef'); - }); - }); - - describe('search hosted', () => { - const repoBuffer = fs.readFileSync( - path.resolve( - __dirname, - '__fixtures__/bitbucket-repo-with-commit-hash.tar.gz', - ), - ); - - beforeEach(() => { - worker.use( - rest.get( - 'https://api.bitbucket.org/2.0/repositories/backstage/mock', - (_, res, ctx) => - res( - ctx.status(200), - ctx.json({ - mainbranch: { - type: 'branch', - name: 'master', - }, - }), - ), - ), - rest.get( - 'https://bitbucket.org/backstage/mock/get/master.tar.gz', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/zip'), - ctx.set( - 'content-disposition', - 'attachment; filename=backstage-mock-12ab34cd56ef.tar.gz', - ), - ctx.body(new Uint8Array(repoBuffer)), - ), - ), - rest.get( - 'https://api.bitbucket.org/2.0/repositories/backstage/mock/commits/master', - (_, res, ctx) => - res( - ctx.status(200), - ctx.json({ - values: [{ hash: '12ab34cd56ef78gh90ij12kl34mn56op78qr90st' }], - }), - ), - ), - ); - }); - - it('works for the naive case', async () => { - const result = await bitbucketProcessor.search( - 'https://bitbucket.org/backstage/mock/src/master/**/index.*', - ); - expect(result.etag).toBe('12ab34cd56ef'); - expect(result.files.length).toBe(1); - expect(result.files[0].url).toBe( - 'https://bitbucket.org/backstage/mock/src/master/docs/index.md', - ); - await expect(result.files[0].content()).resolves.toEqual( - Buffer.from('# Test\n'), - ); - }); - - it('works in nested folders', async () => { - const result = await bitbucketProcessor.search( - 'https://bitbucket.org/backstage/mock/src/master/docs/index.*', - ); - expect(result.etag).toBe('12ab34cd56ef'); - expect(result.files.length).toBe(1); - expect(result.files[0].url).toBe( - 'https://bitbucket.org/backstage/mock/src/master/docs/index.md', - ); - await expect(result.files[0].content()).resolves.toEqual( - Buffer.from('# Test\n'), - ); - }); - - it('throws NotModifiedError when same etag', async () => { - await expect( - bitbucketProcessor.search( - 'https://bitbucket.org/backstage/mock/src/master/**/index.*', - { etag: '12ab34cd56ef' }, - ), - ).rejects.toThrow(NotModifiedError); - }); - }); - - describe('search private', () => { - const privateBitbucketRepoBuffer = fs.readFileSync( - path.resolve(__dirname, '__fixtures__/bitbucket-server-repo.tar.gz'), - ); - - beforeEach(() => { - worker.use( - rest.get( - 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/archive', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/zip'), - ctx.set( - 'content-disposition', - 'attachment; filename=backstage-mock.tgz', - ), - ctx.body(new Uint8Array(privateBitbucketRepoBuffer)), - ), - ), - rest.get( - 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/commits', - (_, res, ctx) => - res( - ctx.status(200), - ctx.json({ - values: [{ id: '12ab34cd56ef78gh90ij12kl34mn56op78qr90st' }], - }), - ), - ), - ); - }); - - it('works for the naive case', async () => { - const result = await hostedBitbucketProcessor.search( - 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/**/index.*?at=master', - ); - expect(result.etag).toBe('12ab34cd56ef'); - expect(result.files.length).toBe(1); - expect(result.files[0].url).toBe( - 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/docs/index.md?at=master', - ); - await expect(result.files[0].content()).resolves.toEqual( - Buffer.from('# Test\n'), - ); - }); - - it('works in nested folders', async () => { - const result = await hostedBitbucketProcessor.search( - 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/docs/index.*?at=master', - ); - expect(result.etag).toBe('12ab34cd56ef'); - expect(result.files.length).toBe(1); - expect(result.files[0].url).toBe( - 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/docs/index.md?at=master', - ); - await expect(result.files[0].content()).resolves.toEqual( - Buffer.from('# Test\n'), - ); - }); - - it('throws NotModifiedError when same etag', async () => { - await expect( - hostedBitbucketProcessor.search( - 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/**/index.*?at=master', - { etag: '12ab34cd56ef' }, - ), - ).rejects.toThrow(NotModifiedError); - }); - - it('should work for exact URLs', async () => { - hostedBitbucketProcessor.readUrl = jest.fn().mockResolvedValue({ - buffer: async () => Buffer.from('content'), - etag: 'etag', - } as UrlReaderServiceReadUrlResponse); - - const result = await hostedBitbucketProcessor.search( - 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/docs/index.md?at=master', - ); - expect(result.etag).toBe('etag'); - expect(result.files.length).toBe(1); - expect(result.files[0].url).toBe( - 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/docs/index.md?at=master', - ); - expect((await result.files[0].content()).toString()).toEqual('content'); - }); - }); -}); diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketUrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketUrlReader.ts deleted file mode 100644 index 8c8843d7e9..0000000000 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketUrlReader.ts +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2020 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 { - UrlReaderService, - UrlReaderServiceReadTreeOptions, - UrlReaderServiceReadTreeResponse, - UrlReaderServiceReadUrlOptions, - UrlReaderServiceReadUrlResponse, - UrlReaderServiceSearchOptions, - UrlReaderServiceSearchResponse, -} from '@backstage/backend-plugin-api'; -import { - assertError, - NotFoundError, - NotModifiedError, -} from '@backstage/errors'; -import { - BitbucketIntegration, - getBitbucketDefaultBranch, - getBitbucketDownloadUrl, - getBitbucketFileFetchUrl, - getBitbucketRequestOptions, - ScmIntegrations, -} from '@backstage/integration'; -import parseGitUrl from 'git-url-parse'; -import { trimEnd } from 'lodash'; -import { Minimatch } from 'minimatch'; -import { LoggerService } from '@backstage/backend-plugin-api'; -import { ReaderFactory, ReadTreeResponseFactory } from './types'; -import { ReadUrlResponseFactory } from './ReadUrlResponseFactory'; - -/** - * Implements a {@link @backstage/backend-plugin-api#UrlReaderService} for files from Bitbucket v1 and v2 APIs, such - * as the one exposed by Bitbucket Cloud itself. - * - * @public - * @deprecated in favor of BitbucketCloudUrlReader and BitbucketServerUrlReader - */ -export class BitbucketUrlReader implements UrlReaderService { - static factory: ReaderFactory = ({ config, logger, treeResponseFactory }) => { - const integrations = ScmIntegrations.fromConfig(config); - return integrations.bitbucket - .list() - .filter( - item => - !integrations.bitbucketCloud.byHost(item.config.host) && - !integrations.bitbucketServer.byHost(item.config.host), - ) - .map(integration => { - const reader = new BitbucketUrlReader(integration, logger, { - treeResponseFactory, - }); - const predicate = (url: URL) => url.host === integration.config.host; - return { reader, predicate }; - }); - }; - - private readonly integration: BitbucketIntegration; - private readonly deps: { treeResponseFactory: ReadTreeResponseFactory }; - - constructor( - integration: BitbucketIntegration, - logger: LoggerService, - deps: { treeResponseFactory: ReadTreeResponseFactory }, - ) { - this.integration = integration; - this.deps = deps; - const { host, token, username, appPassword } = integration.config; - const replacement = - host === 'bitbucket.org' ? 'bitbucketCloud' : 'bitbucketServer'; - logger.warn( - `[Deprecated] Please migrate from "integrations.bitbucket" to "integrations.${replacement}".`, - ); - - if (!token && username && !appPassword) { - throw new Error( - `Bitbucket integration for '${host}' has configured a username but is missing a required appPassword.`, - ); - } - } - - async read(url: string): Promise { - const response = await this.readUrl(url); - return response.buffer(); - } - - private getCredentials = async (options?: { - token?: string; - }): Promise<{ headers: Record }> => { - if (options?.token) { - return { - headers: { - Authorization: `Bearer ${options.token}`, - }, - }; - } - - return await getBitbucketRequestOptions(this.integration.config); - }; - - async readUrl( - url: string, - options?: UrlReaderServiceReadUrlOptions, - ): Promise { - const { etag, lastModifiedAfter, signal } = options ?? {}; - const bitbucketUrl = getBitbucketFileFetchUrl(url, this.integration.config); - const requestOptions = await this.getCredentials(options); - - let response: Response; - try { - response = await fetch(bitbucketUrl.toString(), { - headers: { - ...requestOptions.headers, - ...(etag && { 'If-None-Match': etag }), - ...(lastModifiedAfter && { - 'If-Modified-Since': lastModifiedAfter.toUTCString(), - }), - }, - // TODO(freben): The signal cast is there because pre-3.x versions of - // node-fetch have a very slightly deviating AbortSignal type signature. - // The difference does not affect us in practice however. The cast can be - // removed after we support ESM for CLI dependencies and migrate to - // version 3 of node-fetch. - // https://github.com/backstage/backstage/issues/8242 - ...(signal && { signal: signal as any }), - }); - } catch (e) { - throw new Error(`Unable to read ${url}, ${e}`); - } - - if (response.status === 304) { - throw new NotModifiedError(); - } - - if (response.ok) { - return ReadUrlResponseFactory.fromResponse(response); - } - - const message = `${url} could not be read as ${bitbucketUrl}, ${response.status} ${response.statusText}`; - if (response.status === 404) { - throw new NotFoundError(message); - } - throw new Error(message); - } - - async readTree( - url: string, - options?: UrlReaderServiceReadTreeOptions, - ): Promise { - const { filepath } = parseGitUrl(url); - - const lastCommitShortHash = await this.getLastCommitShortHash(url); - if (options?.etag && options.etag === lastCommitShortHash) { - throw new NotModifiedError(); - } - - const downloadUrl = await getBitbucketDownloadUrl( - url, - this.integration.config, - ); - const archiveBitbucketResponse = await fetch( - downloadUrl, - getBitbucketRequestOptions(this.integration.config), - ); - if (!archiveBitbucketResponse.ok) { - const message = `Failed to read tree from ${url}, ${archiveBitbucketResponse.status} ${archiveBitbucketResponse.statusText}`; - if (archiveBitbucketResponse.status === 404) { - throw new NotFoundError(message); - } - throw new Error(message); - } - - return await this.deps.treeResponseFactory.fromTarArchive({ - response: archiveBitbucketResponse, - subpath: filepath, - etag: lastCommitShortHash, - filter: options?.filter, - }); - } - - async search( - url: string, - options?: UrlReaderServiceSearchOptions, - ): Promise { - const { filepath } = parseGitUrl(url); - - // If it's a direct URL we use readUrl instead - if (!filepath?.match(/[*?]/)) { - try { - const data = await this.readUrl(url, options); - - return { - files: [ - { - url: url, - content: data.buffer, - lastModifiedAt: data.lastModifiedAt, - }, - ], - etag: data.etag ?? '', - }; - } catch (error) { - assertError(error); - if (error.name === 'NotFoundError') { - return { - files: [], - etag: '', - }; - } - throw error; - } - } - - const matcher = new Minimatch(filepath); - - // TODO(freben): For now, read the entire repo and filter through that. In - // a future improvement, we could be smart and try to deduce that non-glob - // prefixes (like for filepaths such as some-prefix/**/a.yaml) can be used - // to get just that part of the repo. - const treeUrl = trimEnd(url.replace(filepath, ''), '/'); - - const tree = await this.readTree(treeUrl, { - etag: options?.etag, - filter: path => matcher.match(path), - }); - const files = await tree.files(); - - return { - etag: tree.etag, - files: files.map(file => ({ - url: this.integration.resolveUrl({ - url: `/${file.path}`, - base: url, - }), - content: file.content, - lastModifiedAt: file.lastModifiedAt, - })), - }; - } - - toString() { - const { host, token, username, appPassword } = this.integration.config; - let authed = Boolean(token); - if (!authed) { - authed = Boolean(username && appPassword); - } - return `bitbucket{host=${host},authed=${authed}}`; - } - - private async getLastCommitShortHash(url: string): Promise { - const { resource, name: repoName, owner: project, ref } = parseGitUrl(url); - - let branch = ref; - if (!branch) { - branch = await getBitbucketDefaultBranch(url, this.integration.config); - } - - const isHosted = resource === 'bitbucket.org'; - // Bitbucket Server https://docs.atlassian.com/bitbucket-server/rest/7.9.0/bitbucket-rest.html#idp222 - const commitsApiUrl = isHosted - ? `${this.integration.config.apiBaseUrl}/repositories/${project}/${repoName}/commits/${branch}` - : `${this.integration.config.apiBaseUrl}/projects/${project}/repos/${repoName}/commits`; - - const commitsResponse = await fetch( - commitsApiUrl, - getBitbucketRequestOptions(this.integration.config), - ); - if (!commitsResponse.ok) { - const message = `Failed to retrieve commits from ${commitsApiUrl}, ${commitsResponse.status} ${commitsResponse.statusText}`; - if (commitsResponse.status === 404) { - throw new NotFoundError(message); - } - throw new Error(message); - } - - const commits = await commitsResponse.json(); - if (isHosted) { - if ( - commits && - commits.values && - commits.values.length > 0 && - commits.values[0].hash - ) { - return commits.values[0].hash.substring(0, 12); - } - } else { - if ( - commits && - commits.values && - commits.values.length > 0 && - commits.values[0].id - ) { - return commits.values[0].id.substring(0, 12); - } - } - - throw new Error(`Failed to read response from ${commitsApiUrl}`); - } -} diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/UrlReaders.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/UrlReaders.ts index 229bdd4903..f04a668b0a 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/UrlReaders.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/UrlReaders.ts @@ -24,7 +24,6 @@ import { UrlReaderPredicateMux } from './UrlReaderPredicateMux'; import { AzureUrlReader } from './AzureUrlReader'; import { BitbucketCloudUrlReader } from './BitbucketCloudUrlReader'; import { BitbucketServerUrlReader } from './BitbucketServerUrlReader'; -import { BitbucketUrlReader } from './BitbucketUrlReader'; import { GerritUrlReader } from './GerritUrlReader'; import { GithubUrlReader } from './GithubUrlReader'; import { GitlabUrlReader } from './GitlabUrlReader'; @@ -92,7 +91,6 @@ export class UrlReaders { AzureUrlReader.factory, BitbucketCloudUrlReader.factory, BitbucketServerUrlReader.factory, - BitbucketUrlReader.factory, GerritUrlReader.factory, GithubUrlReader.factory, GiteaUrlReader.factory, diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/index.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/index.ts index fc33efbc3a..3f5d7a7ea5 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/index.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/index.ts @@ -16,7 +16,6 @@ export { AzureUrlReader } from './AzureUrlReader'; export { BitbucketCloudUrlReader } from './BitbucketCloudUrlReader'; -export { BitbucketUrlReader } from './BitbucketUrlReader'; export { BitbucketServerUrlReader } from './BitbucketServerUrlReader'; export { GerritUrlReader } from './GerritUrlReader'; export { GithubUrlReader } from './GithubUrlReader'; diff --git a/packages/integration-react/dev/DevPage.tsx b/packages/integration-react/dev/DevPage.tsx index d03baca1d7..1c94ef984a 100644 --- a/packages/integration-react/dev/DevPage.tsx +++ b/packages/integration-react/dev/DevPage.tsx @@ -16,7 +16,7 @@ import { ScmIntegration, ScmIntegrationsGroup } from '@backstage/integration'; import Typography from '@material-ui/core/Typography'; -import { scmIntegrationsApiRef } from '../src/ScmIntegrationsApi'; +import { scmIntegrationsApiRef } from '../src/api/ScmIntegrationsApi'; import { Content } from '@backstage/core-components'; import { useApi } from '@backstage/core-plugin-api'; @@ -48,10 +48,6 @@ export const DevPage = () => { Azure - - Bitbucket - - Bitbucket Cloud diff --git a/packages/integration-react/dev/index.tsx b/packages/integration-react/dev/index.tsx index 65486d1f06..fbff7ef0c6 100644 --- a/packages/integration-react/dev/index.tsx +++ b/packages/integration-react/dev/index.tsx @@ -15,7 +15,7 @@ */ import { createDevApp } from '@backstage/dev-utils'; import { ScmIntegrations } from '@backstage/integration'; -import { scmIntegrationsApiRef } from '../src/ScmIntegrationsApi'; +import { scmIntegrationsApiRef } from '../src/api/ScmIntegrationsApi'; import { DevPage } from './DevPage'; import { configApiRef, createApiFactory } from '@backstage/core-plugin-api'; diff --git a/packages/integration-react/src/api/ScmIntegrationsApi.test.ts b/packages/integration-react/src/api/ScmIntegrationsApi.test.ts index 55acd29eec..0a1f1b1b8f 100644 --- a/packages/integration-react/src/api/ScmIntegrationsApi.test.ts +++ b/packages/integration-react/src/api/ScmIntegrationsApi.test.ts @@ -26,6 +26,6 @@ describe('scmIntegrationsApiRef', () => { it('should be instantiated', () => { const i = ScmIntegrationsApi.fromConfig(new ConfigReader({})); - expect(i.list().length).toBe(8); // The default ones + expect(i.list().length).toBe(7); // The default ones }); }); diff --git a/packages/integration/config.d.ts b/packages/integration/config.d.ts index add237a6b9..df7a3aa4af 100644 --- a/packages/integration/config.d.ts +++ b/packages/integration/config.d.ts @@ -27,27 +27,6 @@ export interface Config { * @visibility frontend */ host: string; - /** - * Token used to authenticate requests. - * @visibility secret - * @deprecated Use `credentials` instead. - */ - token?: string; - - /** - * The credential to use for requests. - * - * If no credential is specified anonymous access is used. - * - * @deepVisibility secret - * @deprecated Use `credentials` instead. - */ - credential?: { - clientId?: string; - clientSecret?: string; - tenantId?: string; - personalAccessToken?: string; - }; /** * The credentials to use for requests. If multiple credentials are specified the first one that matches the organization is used. @@ -131,43 +110,6 @@ export interface Config { }; }>; - /** - * Integration configuration for Bitbucket - * @deprecated replaced by bitbucketCloud and bitbucketServer - */ - bitbucket?: Array<{ - /** - * The hostname of the given Bitbucket instance - * @visibility frontend - */ - host: string; - /** - * Token used to authenticate requests. - * @visibility secret - */ - token?: string; - /** - * The base url for the Bitbucket API, for example https://api.bitbucket.org/2.0 - * @visibility frontend - */ - apiBaseUrl?: string; - /** - * The username to use for authenticated requests. - * @visibility secret - */ - username?: string; - /** - * Bitbucket app password used to authenticate requests. - * @visibility secret - */ - appPassword?: string; - /** - * PGP signing key for signing commits. - * @visibility secret - */ - commitSigningKey?: string; - }>; - /** Integration configuration for Bitbucket Cloud */ bitbucketCloud?: Array<{ /** diff --git a/packages/integration/report.api.md b/packages/integration/report.api.md index 83c75db4d1..df1c7e9112 100644 --- a/packages/integration/report.api.md +++ b/packages/integration/report.api.md @@ -201,8 +201,6 @@ export class AzureIntegration implements ScmIntegration { // @public export type AzureIntegrationConfig = { host: string; - token?: string; - credential?: AzureDevOpsCredential; credentials?: AzureDevOpsCredential[]; commitSigningKey?: string; }; @@ -255,37 +253,6 @@ export type BitbucketCloudIntegrationConfig = { commitSigningKey?: string; }; -// @public @deprecated -export class BitbucketIntegration implements ScmIntegration { - constructor(integrationConfig: BitbucketIntegrationConfig); - // (undocumented) - get config(): BitbucketIntegrationConfig; - // (undocumented) - static factory: ScmIntegrationsFactory; - // (undocumented) - resolveEditUrl(url: string): string; - // (undocumented) - resolveUrl(options: { - url: string; - base: string; - lineNumber?: number; - }): string; - // (undocumented) - get title(): string; - // (undocumented) - get type(): string; -} - -// @public @deprecated -export type BitbucketIntegrationConfig = { - host: string; - apiBaseUrl: string; - token?: string; - username?: string; - appPassword?: string; - commitSigningKey?: string; -}; - // @public export class BitbucketServerIntegration implements ScmIntegration { constructor(integrationConfig: BitbucketServerIntegrationConfig); @@ -317,14 +284,6 @@ export type BitbucketServerIntegrationConfig = { commitSigningKey?: string; }; -// @public @deprecated -export function buildGerritGitilesArchiveUrl( - config: GerritIntegrationConfig, - project: string, - branch: string, - filePath: string, -): string; - // @public export function buildGerritGitilesArchiveUrlFromLocation( config: GerritIntegrationConfig, @@ -426,14 +385,6 @@ export function getAzureDownloadUrl(url: string): string; // @public export function getAzureFileFetchUrl(url: string): string; -// @public @deprecated -export function getAzureRequestOptions( - config: AzureIntegrationConfig, - additionalHeaders?: Record, -): Promise<{ - headers: Record; -}>; - // @public export function getBitbucketCloudDefaultBranch( url: string, @@ -465,31 +416,6 @@ export function getBitbucketCloudRequestOptions( headers: Record; }>; -// @public @deprecated -export function getBitbucketDefaultBranch( - url: string, - config: BitbucketIntegrationConfig, -): Promise; - -// @public @deprecated -export function getBitbucketDownloadUrl( - url: string, - config: BitbucketIntegrationConfig, -): Promise; - -// @public @deprecated -export function getBitbucketFileFetchUrl( - url: string, - config: BitbucketIntegrationConfig, -): string; - -// @public @deprecated -export function getBitbucketRequestOptions( - config: BitbucketIntegrationConfig, -): { - headers: Record; -}; - // @public export function getBitbucketServerDefaultBranch( url: string, @@ -579,14 +505,6 @@ export function getGithubFileFetchUrl( credentials: GithubCredentials, ): string; -// @public @deprecated -export function getGitHubRequestOptions( - config: GithubIntegrationConfig, - credentials: GithubCredentials, -): { - headers: Record; -}; - // @public export function getGitilesAuthenticationUrl( config: GerritIntegrationConfig, @@ -854,8 +772,6 @@ export interface IntegrationsByType { azure: ScmIntegrationsGroup; // (undocumented) azureBlobStorage: ScmIntegrationsGroup; - // @deprecated (undocumented) - bitbucket: ScmIntegrationsGroup; // (undocumented) bitbucketCloud: ScmIntegrationsGroup; // (undocumented) @@ -874,16 +790,6 @@ export interface IntegrationsByType { harness: ScmIntegrationsGroup; } -// @public @deprecated -export function parseGerritGitilesUrl( - config: GerritIntegrationConfig, - url: string, -): { - branch: string; - filePath: string; - project: string; -}; - // @public export function parseGerritJsonResponse(response: Response): Promise; @@ -989,16 +895,6 @@ export function readBitbucketCloudIntegrationConfigs( configs: Config[], ): BitbucketCloudIntegrationConfig[]; -// @public @deprecated -export function readBitbucketIntegrationConfig( - config: Config, -): BitbucketIntegrationConfig; - -// @public @deprecated -export function readBitbucketIntegrationConfigs( - configs: Config[], -): BitbucketIntegrationConfig[]; - // @public export function readBitbucketServerIntegrationConfig( config: Config, @@ -1085,8 +981,6 @@ export interface ScmIntegrationRegistry azure: ScmIntegrationsGroup; // (undocumented) azureBlobStorage: ScmIntegrationsGroup; - // @deprecated (undocumented) - bitbucket: ScmIntegrationsGroup; // (undocumented) bitbucketCloud: ScmIntegrationsGroup; // (undocumented) @@ -1120,8 +1014,6 @@ export class ScmIntegrations implements ScmIntegrationRegistry { get azure(): ScmIntegrationsGroup; // (undocumented) get azureBlobStorage(): ScmIntegrationsGroup; - // @deprecated (undocumented) - get bitbucket(): ScmIntegrationsGroup; // (undocumented) get bitbucketCloud(): ScmIntegrationsGroup; // (undocumented) diff --git a/packages/integration/src/ScmIntegrations.test.ts b/packages/integration/src/ScmIntegrations.test.ts index 7f75ab44fc..254d774a80 100644 --- a/packages/integration/src/ScmIntegrations.test.ts +++ b/packages/integration/src/ScmIntegrations.test.ts @@ -21,8 +21,6 @@ import { BitbucketCloudIntegration, BitbucketCloudIntegrationConfig, } from './bitbucketCloud'; -import { BitbucketIntegrationConfig } from './bitbucket'; -import { BitbucketIntegration } from './bitbucket/BitbucketIntegration'; import { BitbucketServerIntegration, BitbucketServerIntegrationConfig, @@ -62,10 +60,6 @@ describe('ScmIntegrations', () => { host: 'azureblobstorage.local', } as AzureBlobStorageIntegrationConfig); - const bitbucket = new BitbucketIntegration({ - host: 'bitbucket.local', - } as BitbucketIntegrationConfig); - const bitbucketCloud = new BitbucketCloudIntegration({ host: 'bitbucket.org', } as BitbucketCloudIntegrationConfig); @@ -103,7 +97,6 @@ describe('ScmIntegrations', () => { awsCodeCommit: basicIntegrations([awsCodeCommit], item => item.config.host), azure: basicIntegrations([azure], item => item.config.host), azureBlobStorage: basicIntegrations([azureBlob], item => item.config.host), - bitbucket: basicIntegrations([bitbucket], item => item.config.host), bitbucketCloud: basicIntegrations([bitbucketCloud], item => item.title), bitbucketServer: basicIntegrations( [bitbucketServer], @@ -126,7 +119,6 @@ describe('ScmIntegrations', () => { expect(i.azureBlobStorage.byUrl('https://azureblobstorage.local')).toBe( azureBlob, ); - expect(i.bitbucket.byUrl('https://bitbucket.local')).toBe(bitbucket); expect(i.bitbucketCloud.byUrl('https://bitbucket.org')).toBe( bitbucketCloud, ); @@ -147,7 +139,6 @@ describe('ScmIntegrations', () => { awsCodeCommit, azure, azureBlob, - bitbucket, bitbucketCloud, bitbucketServer, gerrit, @@ -166,7 +157,6 @@ describe('ScmIntegrations', () => { expect(i.azureBlobStorage.byUrl('https://azureblobstorage.local')).toBe( azureBlob, ); - expect(i.byUrl('https://bitbucket.local')).toBe(bitbucket); expect(i.byUrl('https://bitbucket.org')).toBe(bitbucketCloud); expect(i.byUrl('https://bitbucket-server.local')).toBe(bitbucketServer); expect(i.byUrl('https://gerrit.local')).toBe(gerrit); @@ -179,7 +169,6 @@ describe('ScmIntegrations', () => { expect(i.byHost('awscodecommit.local')).toBe(awsCodeCommit); expect(i.byHost('azure.local')).toBe(azure); expect(i.byHost('azureblobstorage.local')).toBe(azureBlob); - expect(i.byHost('bitbucket.local')).toBe(bitbucket); expect(i.byHost('bitbucket.org')).toBe(bitbucketCloud); expect(i.byHost('bitbucket-server.local')).toBe(bitbucketServer); expect(i.byHost('gerrit.local')).toBe(gerrit); diff --git a/packages/integration/src/ScmIntegrations.ts b/packages/integration/src/ScmIntegrations.ts index 573975da0e..26a278fdbf 100644 --- a/packages/integration/src/ScmIntegrations.ts +++ b/packages/integration/src/ScmIntegrations.ts @@ -19,7 +19,6 @@ import { AwsS3Integration } from './awsS3/AwsS3Integration'; import { AwsCodeCommitIntegration } from './awsCodeCommit/AwsCodeCommitIntegration'; import { AzureIntegration } from './azure/AzureIntegration'; import { BitbucketCloudIntegration } from './bitbucketCloud/BitbucketCloudIntegration'; -import { BitbucketIntegration } from './bitbucket/BitbucketIntegration'; import { BitbucketServerIntegration } from './bitbucketServer/BitbucketServerIntegration'; import { GerritIntegration } from './gerrit/GerritIntegration'; import { GithubIntegration } from './github/GithubIntegration'; @@ -42,10 +41,6 @@ export interface IntegrationsByType { awsCodeCommit: ScmIntegrationsGroup; azureBlobStorage: ScmIntegrationsGroup; azure: ScmIntegrationsGroup; - /** - * @deprecated in favor of `bitbucketCloud` and `bitbucketServer` - */ - bitbucket: ScmIntegrationsGroup; bitbucketCloud: ScmIntegrationsGroup; bitbucketServer: ScmIntegrationsGroup; gerrit: ScmIntegrationsGroup; @@ -70,7 +65,6 @@ export class ScmIntegrations implements ScmIntegrationRegistry { awsCodeCommit: AwsCodeCommitIntegration.factory({ config }), azureBlobStorage: AzureBlobStorageIntergation.factory({ config }), azure: AzureIntegration.factory({ config }), - bitbucket: BitbucketIntegration.factory({ config }), bitbucketCloud: BitbucketCloudIntegration.factory({ config }), bitbucketServer: BitbucketServerIntegration.factory({ config }), gerrit: GerritIntegration.factory({ config }), @@ -102,13 +96,6 @@ export class ScmIntegrations implements ScmIntegrationRegistry { return this.byType.azure; } - /** - * @deprecated in favor of `bitbucketCloud()` and `bitbucketServer()` - */ - get bitbucket(): ScmIntegrationsGroup { - return this.byType.bitbucket; - } - get bitbucketCloud(): ScmIntegrationsGroup { return this.byType.bitbucketCloud; } @@ -148,20 +135,10 @@ export class ScmIntegrations implements ScmIntegrationRegistry { } byUrl(url: string | URL): ScmIntegration | undefined { - let candidates = Object.values(this.byType) + const candidates = Object.values(this.byType) .map(i => i.byUrl(url)) .filter(Boolean); - // Do not return deprecated integrations if there are other options - if (candidates.length > 1) { - const filteredCandidates = candidates.filter( - x => !(x instanceof BitbucketIntegration), - ); - if (filteredCandidates.length !== 0) { - candidates = filteredCandidates; - } - } - return candidates[0]; } diff --git a/packages/integration/src/azure/config.test.ts b/packages/integration/src/azure/config.test.ts index c1ddf1590f..cd2d916837 100644 --- a/packages/integration/src/azure/config.test.ts +++ b/packages/integration/src/azure/config.test.ts @@ -276,50 +276,6 @@ describe('readAzureIntegrationConfig', () => { expect(output).toEqual({ host: 'dev.azure.com' }); }); - it('maps deprecated token to credentials', () => { - const output = readAzureIntegrationConfig( - buildConfig({ - host: 'dev.azure.com', - token: 't', - }), - ); - - expect(output).toEqual({ - host: 'dev.azure.com', - credentials: [ - { - kind: 'PersonalAccessToken', - personalAccessToken: 't', - }, - ], - }); - }); - - it('maps deprecated credential to credentials', () => { - const output = readAzureIntegrationConfig( - buildConfig({ - host: 'dev.azure.com', - credential: { - clientId: 'id', - clientSecret: 'secret', - tenantId: 'tenantId', - }, - }), - ); - - expect(output).toEqual({ - host: 'dev.azure.com', - credentials: [ - { - kind: 'ClientSecret', - clientId: 'id', - clientSecret: 'secret', - tenantId: 'tenantId', - }, - ], - }); - }); - it('rejects config when host is not valid', () => { expect(() => readAzureIntegrationConfig(buildConfig({ ...valid, host: 7 })), diff --git a/packages/integration/src/azure/config.ts b/packages/integration/src/azure/config.ts index 09b97755ec..0213475cc8 100644 --- a/packages/integration/src/azure/config.ts +++ b/packages/integration/src/azure/config.ts @@ -32,24 +32,6 @@ export type AzureIntegrationConfig = { */ host: string; - /** - * The authorization token to use for requests. - * - * If no token is specified, anonymous access is used. - * - * @deprecated Use `credentials` instead. - */ - token?: string; - - /** - * The credential to use for requests. - * - * If no credential is specified anonymous access is used. - * - * @deprecated Use `credentials` instead. - */ - credential?: AzureDevOpsCredential; - /** * The credentials to use for requests. If multiple credentials are specified the first one that matches the organization is used. * If not organization matches the first credential without an organization is used. diff --git a/packages/integration/src/azure/deprecated.test.ts b/packages/integration/src/azure/deprecated.test.ts deleted file mode 100644 index 4411ef5f2f..0000000000 --- a/packages/integration/src/azure/deprecated.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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 { getAzureRequestOptions } from './deprecated'; -import { DateTime } from 'luxon'; -import { - AccessToken, - ClientSecretCredential, - ManagedIdentityCredential, -} from '@azure/identity'; - -jest.mock('@azure/identity'); - -const MockedClientSecretCredential = ClientSecretCredential as jest.MockedClass< - typeof ClientSecretCredential ->; - -const MockedManagedIdentityCredential = - ManagedIdentityCredential as jest.MockedClass< - typeof ManagedIdentityCredential - >; - -describe('azure core', () => { - beforeEach(() => { - jest.resetAllMocks(); - - MockedClientSecretCredential.prototype.getToken.mockImplementation(() => - Promise.resolve({ - expiresOnTimestamp: DateTime.local().plus({ days: 1 }).toSeconds(), - token: 'fake-client-secret-token', - } as AccessToken), - ); - MockedManagedIdentityCredential.prototype.getToken.mockImplementation(() => - Promise.resolve({ - expiresOnTimestamp: DateTime.local().plus({ days: 1 }).toSeconds(), - token: 'fake-managed-identity-token', - } as AccessToken), - ); - }); - - describe('getAzureRequestOptions', () => { - it('should not add authorization header when not using token or credential', async () => { - expect(await getAzureRequestOptions({ host: '' })).toEqual( - expect.objectContaining({ - headers: expect.not.objectContaining({ - Authorization: expect.anything(), - }), - }), - ); - }); - - it('should add authorization header when using a personal access token', async () => { - const pat = '0123456789'; - const encoded = Buffer.from(`:${pat}`).toString('base64'); - expect( - await getAzureRequestOptions({ - host: '', - credentials: [ - { - kind: 'PersonalAccessToken', - personalAccessToken: pat, - }, - ], - }), - ).toEqual( - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: `Basic ${encoded}`, - }), - }), - ); - }); - - it('should add authorization header when using a client secret', async () => { - expect( - await getAzureRequestOptions({ - host: '', - credentials: [ - { - kind: 'ClientSecret', - clientId: 'fake-id', - clientSecret: 'fake-secret', - tenantId: 'fake-tenant', - }, - ], - }), - ).toEqual( - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: 'Bearer fake-client-secret-token', - }), - }), - ); - }); - - it('should add authorization header when using a managed identity', async () => { - expect( - await getAzureRequestOptions({ - host: '', - credentials: [ - { - kind: 'ManagedIdentity', - clientId: 'fake-id', - }, - ], - }), - ).toEqual( - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: 'Bearer fake-managed-identity-token', - }), - }), - ); - }); - }); -}); diff --git a/packages/integration/src/azure/deprecated.ts b/packages/integration/src/azure/deprecated.ts deleted file mode 100644 index fd19f3dc14..0000000000 --- a/packages/integration/src/azure/deprecated.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 { AzureIntegrationConfig } from './config'; -import { CachedAzureDevOpsCredentialsProvider } from './CachedAzureDevOpsCredentialsProvider'; - -/** - * Gets the request options necessary to make requests to a given provider. - * - * @param config - The relevant provider config - * @param additionalHeaders - Additional headers for the request - * @public - * @deprecated Use {@link AzureDevOpsCredentialsProvider} instead. - */ -export async function getAzureRequestOptions( - config: AzureIntegrationConfig, - additionalHeaders?: Record, -): Promise<{ headers: Record }> { - const headers: Record = additionalHeaders - ? { ...additionalHeaders } - : {}; - - /* - * Since we do not have a way to determine which organization the request is for, - * we will use the first credential that does not have an organization specified. - */ - const credentialConfig = config.credentials?.filter( - credential => - credential.organizations === undefined || - credential.organizations.length === 0, - )[0]; - - if (credentialConfig) { - const credentialsProvider = - CachedAzureDevOpsCredentialsProvider.fromAzureDevOpsCredential( - credentialConfig, - ); - const credentials = await credentialsProvider.getCredentials(); - - return { - headers: { - ...credentials?.headers, - ...headers, - }, - }; - } - - return { headers }; -} diff --git a/packages/integration/src/azure/index.ts b/packages/integration/src/azure/index.ts index a447c8ffd8..b04ead862f 100644 --- a/packages/integration/src/azure/index.ts +++ b/packages/integration/src/azure/index.ts @@ -38,5 +38,3 @@ export { export * from './types'; export { DefaultAzureDevOpsCredentialsProvider } from './DefaultAzureDevOpsCredentialsProvider'; - -export * from './deprecated'; diff --git a/packages/integration/src/bitbucket/BitbucketIntegration.test.ts b/packages/integration/src/bitbucket/BitbucketIntegration.test.ts deleted file mode 100644 index 575e1c9b6f..0000000000 --- a/packages/integration/src/bitbucket/BitbucketIntegration.test.ts +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2020 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 { ConfigReader } from '@backstage/config'; -import { BitbucketIntegration } from './BitbucketIntegration'; - -describe('BitbucketIntegration', () => { - describe('factory', () => { - it('works', () => { - const integrations = BitbucketIntegration.factory({ - config: new ConfigReader({ - integrations: { - bitbucket: [ - { - host: 'h.com', - apiBaseUrl: 'a', - token: 't', - username: 'u', - appPassword: 'p', - }, - ], - }, - }), - }); - expect(integrations.list().length).toBe(2); // including default - expect(integrations.list()[0].config.host).toBe('h.com'); - expect(integrations.list()[1].config.host).toBe('bitbucket.org'); - }); - - it('falls back to bitbucketCloud+bitbucketServer', () => { - const integrations = BitbucketIntegration.factory({ - config: new ConfigReader({ - integrations: { - bitbucketCloud: [ - { - username: 'u', - appPassword: 'p', - }, - ], - bitbucketServer: [ - { - host: 'h.com', - apiBaseUrl: 'a', - token: 't', - }, - ], - }, - }), - }); - expect(integrations.list().length).toBe(2); // including default - expect(integrations.list()[0].config.host).toBe('bitbucket.org'); - expect(integrations.list()[1].config.host).toBe('h.com'); - }); - }); - - it('returns the basics', () => { - const integration = new BitbucketIntegration({ host: 'h.com' } as any); - expect(integration.type).toBe('bitbucket'); - expect(integration.title).toBe('h.com'); - }); - - it('resolves url line number correctly for Bitbucket Cloud', () => { - const integration = new BitbucketIntegration({ - host: 'bitbucket.org', - } as any); - - expect( - integration.resolveUrl({ - url: './a.yaml', - base: 'https://bitbucket.org/my-owner/my-project/src/master/README.md', - lineNumber: 14, - }), - ).toBe( - 'https://bitbucket.org/my-owner/my-project/src/master/a.yaml#lines-14', - ); - }); - - it('resolves url line number correctly for Bitbucket Server', () => { - const integration = new BitbucketIntegration({ host: 'h.com' } as any); - - expect( - integration.resolveUrl({ - url: './a.yaml', - base: 'https://bitbucket.org/my-owner/my-project/src/master/README.md', - lineNumber: 14, - }), - ).toBe('https://bitbucket.org/my-owner/my-project/src/master/a.yaml#14'); - }); - - it('resolve edit URL', () => { - const integration = new BitbucketIntegration({ host: 'h.com' } as any); - - expect( - integration.resolveEditUrl( - 'https://bitbucket.org/my-owner/my-project/src/master/README.md', - ), - ).toBe( - 'https://bitbucket.org/my-owner/my-project/src/master/README.md?mode=edit&spa=0&at=master', - ); - }); -}); diff --git a/packages/integration/src/bitbucket/BitbucketIntegration.ts b/packages/integration/src/bitbucket/BitbucketIntegration.ts deleted file mode 100644 index d92532b54a..0000000000 --- a/packages/integration/src/bitbucket/BitbucketIntegration.ts +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2020 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 parseGitUrl from 'git-url-parse'; -import { basicIntegrations, defaultScmResolveUrl } from '../helpers'; -import { ScmIntegration, ScmIntegrationsFactory } from '../types'; -import { - BitbucketIntegrationConfig, - readBitbucketIntegrationConfigs, -} from './config'; - -/** - * A Bitbucket based integration. - * - * @public - * @deprecated replaced by the integrations bitbucketCloud and bitbucketServer. - */ -export class BitbucketIntegration implements ScmIntegration { - static factory: ScmIntegrationsFactory = ({ - config, - }) => { - const configs = readBitbucketIntegrationConfigs( - config.getOptionalConfigArray('integrations.bitbucket') ?? [ - // if integrations.bitbucket was not used assume the use was migrated to the new configs - // and backport for the deprecated integration to be usable for other parts of the system - // until these got migrated - ...(config.getOptionalConfigArray('integrations.bitbucketCloud') ?? []), - ...(config.getOptionalConfigArray('integrations.bitbucketServer') ?? - []), - ], - ); - return basicIntegrations( - configs.map(c => new BitbucketIntegration(c)), - i => i.config.host, - ); - }; - - constructor(private readonly integrationConfig: BitbucketIntegrationConfig) {} - - get type(): string { - return 'bitbucket'; - } - - get title(): string { - return this.integrationConfig.host; - } - - get config(): BitbucketIntegrationConfig { - return this.integrationConfig; - } - - resolveUrl(options: { - url: string; - base: string; - lineNumber?: number; - }): string { - const resolved = defaultScmResolveUrl(options); - if (!options.lineNumber) { - return resolved; - } - - const url = new URL(resolved); - - if (this.integrationConfig.host === 'bitbucket.org') { - // Bitbucket Cloud uses the syntax #lines-{start}[:{end}][,...] - url.hash = `lines-${options.lineNumber}`; - } else { - // Bitbucket Server uses the syntax #{start}[-{end}][,...] - url.hash = `${options.lineNumber}`; - } - - return url.toString(); - } - - resolveEditUrl(url: string): string { - const urlData = parseGitUrl(url); - const editUrl = new URL(url); - - editUrl.searchParams.set('mode', 'edit'); - // TODO: Not sure what spa=0 does, at least bitbucket.org doesn't support it - // but this is taken over from the initial implementation. - editUrl.searchParams.set('spa', '0'); - editUrl.searchParams.set('at', urlData.ref); - return editUrl.toString(); - } -} diff --git a/packages/integration/src/bitbucket/config.test.ts b/packages/integration/src/bitbucket/config.test.ts deleted file mode 100644 index 8dd20cade7..0000000000 --- a/packages/integration/src/bitbucket/config.test.ts +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2020 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 { Config, ConfigReader } from '@backstage/config'; -import { loadConfigSchema } from '@backstage/config-loader'; -import { - BitbucketIntegrationConfig, - readBitbucketIntegrationConfig, - readBitbucketIntegrationConfigs, -} from './config'; - -describe('readBitbucketIntegrationConfig', () => { - function buildConfig(data: Partial): Config { - return new ConfigReader(data); - } - - async function buildFrontendConfig( - data: Partial, - ): Promise { - const fullSchema = await loadConfigSchema({ - dependencies: ['@backstage/integration'], - }); - const serializedSchema = fullSchema.serialize() as { - schemas: { value: { properties?: { integrations?: object } } }[]; - }; - const schema = await loadConfigSchema({ - serialized: { - ...serializedSchema, // only include schemas that apply to integrations - schemas: serializedSchema.schemas.filter( - s => s.value?.properties?.integrations, - ), - }, - }); - const processed = schema.process( - [{ data: { integrations: { bitbucket: [data] } }, context: 'app' }], - { visibility: ['frontend'] }, - ); - return new ConfigReader( - (processed[0].data as any).integrations.bitbucket[0], - ); - } - - it('reads all values', () => { - const output = readBitbucketIntegrationConfig( - buildConfig({ - host: 'a.com', - apiBaseUrl: 'https://a.com/api', - token: 't\n\n\n', - username: 'u', - appPassword: '\n\n\np', - }), - ); - expect(output).toEqual({ - host: 'a.com', - apiBaseUrl: 'https://a.com/api', - token: 't', - username: 'u', - appPassword: 'p', - }); - }); - - it('inserts the defaults if missing', () => { - const output = readBitbucketIntegrationConfig(buildConfig({})); - expect(output).toEqual( - expect.objectContaining({ - host: 'bitbucket.org', - apiBaseUrl: 'https://api.bitbucket.org/2.0', - }), - ); - }); - - it('rejects funky configs', () => { - const valid: any = { - host: 'a.com', - apiBaseUrl: 'https://a.com/api', - token: 't', - username: 'u', - appPassword: 'p', - }; - expect(() => - readBitbucketIntegrationConfig(buildConfig({ ...valid, host: 7 })), - ).toThrow(/host/); - expect(() => - readBitbucketIntegrationConfig(buildConfig({ ...valid, apiBaseUrl: 7 })), - ).toThrow(/apiBaseUrl/); - expect(() => - readBitbucketIntegrationConfig(buildConfig({ ...valid, token: 7 })), - ).toThrow(/token/); - expect(() => - readBitbucketIntegrationConfig(buildConfig({ ...valid, username: 7 })), - ).toThrow(/username/); - expect(() => - readBitbucketIntegrationConfig(buildConfig({ ...valid, appPassword: 7 })), - ).toThrow(/appPassword/); - }); - - it('works on the frontend', async () => { - expect( - readBitbucketIntegrationConfig( - await buildFrontendConfig({ - host: 'a.com', - apiBaseUrl: 'https://a.com/api', - token: 't', - username: 'u', - appPassword: 'p', - }), - ), - ).toEqual({ - host: 'a.com', - apiBaseUrl: 'https://a.com/api', - }); - }); -}); - -describe('readBitbucketIntegrationConfigs', () => { - function buildConfig(data: Partial[]): Config[] { - return data.map(item => new ConfigReader(item)); - } - - it('reads all values', () => { - const output = readBitbucketIntegrationConfigs( - buildConfig([ - { - host: 'a.com', - apiBaseUrl: 'https://a.com/api', - token: 't', - username: 'u', - appPassword: 'p', - }, - ]), - ); - expect(output).toContainEqual({ - host: 'a.com', - apiBaseUrl: 'https://a.com/api', - token: 't', - username: 'u', - appPassword: 'p', - }); - }); - - it('adds a default Bitbucket Cloud entry when missing', () => { - const output = readBitbucketIntegrationConfigs(buildConfig([])); - expect(output).toEqual([ - { - host: 'bitbucket.org', - apiBaseUrl: 'https://api.bitbucket.org/2.0', - }, - ]); - }); - - it('injects the correct Bitbucket Cloud API base URL when missing', () => { - const output = readBitbucketIntegrationConfigs( - buildConfig([{ host: 'bitbucket.org' }]), - ); - expect(output).toEqual([ - { - host: 'bitbucket.org', - apiBaseUrl: 'https://api.bitbucket.org/2.0', - }, - ]); - }); -}); diff --git a/packages/integration/src/bitbucket/config.ts b/packages/integration/src/bitbucket/config.ts deleted file mode 100644 index 6939034e7d..0000000000 --- a/packages/integration/src/bitbucket/config.ts +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2020 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 { Config } from '@backstage/config'; -import { trimEnd } from 'lodash'; -import { isValidHost } from '../helpers'; - -const BITBUCKET_HOST = 'bitbucket.org'; -const BITBUCKET_API_BASE_URL = 'https://api.bitbucket.org/2.0'; - -/** - * The configuration parameters for a single Bitbucket API provider. - * - * @public - * @deprecated bitbucket integration replaced by integrations bitbucketCloud and bitbucketServer. - */ -export type BitbucketIntegrationConfig = { - /** - * The host of the target that this matches on, e.g. "bitbucket.org" - */ - host: string; - - /** - * The base URL of the API of this provider, e.g. "https://api.bitbucket.org/2.0", - * with no trailing slash. - * - * Values omitted at the optional property at the app-config will be deduced - * from the "host" value. - */ - apiBaseUrl: string; - - /** - * The authorization token to use for requests to a Bitbucket Server provider. - * - * See https://confluence.atlassian.com/bitbucketserver/personal-access-tokens-939515499.html - * - * If no token is specified, anonymous access is used. - */ - token?: string; - - /** - * The username to use for requests to Bitbucket Cloud (bitbucket.org). - */ - username?: string; - - /** - * Authentication with Bitbucket Cloud (bitbucket.org) is done using app passwords. - * - * See https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/ - */ - appPassword?: string; - - /** - * Signing key for commits - */ - commitSigningKey?: string; -}; - -/** - * Reads a single Bitbucket integration config. - * - * @param config - The config object of a single integration - * @public - * @deprecated bitbucket integration replaced by integrations bitbucketCloud and bitbucketServer. - */ -export function readBitbucketIntegrationConfig( - config: Config, -): BitbucketIntegrationConfig { - const host = config.getOptionalString('host') ?? BITBUCKET_HOST; - let apiBaseUrl = config.getOptionalString('apiBaseUrl'); - const token = config.getOptionalString('token')?.trim(); - const username = config.getOptionalString('username'); - const appPassword = config.getOptionalString('appPassword')?.trim(); - - if (!isValidHost(host)) { - throw new Error( - `Invalid Bitbucket integration config, '${host}' is not a valid host`, - ); - } - - if (apiBaseUrl) { - apiBaseUrl = trimEnd(apiBaseUrl, '/'); - } else if (host === BITBUCKET_HOST) { - apiBaseUrl = BITBUCKET_API_BASE_URL; - } else { - apiBaseUrl = `https://${host}/rest/api/1.0`; - } - - return { - host, - apiBaseUrl, - token, - username, - appPassword, - commitSigningKey: config.getOptionalString('commitSigningKey'), - }; -} - -/** - * Reads a set of Bitbucket integration configs, and inserts some defaults for - * public Bitbucket if not specified. - * - * @param configs - All of the integration config objects - * @public - * @deprecated bitbucket integration replaced by integrations bitbucketCloud and bitbucketServer. - */ -export function readBitbucketIntegrationConfigs( - configs: Config[], -): BitbucketIntegrationConfig[] { - // First read all the explicit integrations - const result = configs.map(readBitbucketIntegrationConfig); - - // If no explicit bitbucket.org integration was added, put one in the list as - // a convenience - if (!result.some(c => c.host === BITBUCKET_HOST)) { - result.push({ - host: BITBUCKET_HOST, - apiBaseUrl: BITBUCKET_API_BASE_URL, - }); - } - - return result; -} diff --git a/packages/integration/src/bitbucket/core.test.ts b/packages/integration/src/bitbucket/core.test.ts deleted file mode 100644 index f1b4e04f15..0000000000 --- a/packages/integration/src/bitbucket/core.test.ts +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright 2020 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 { rest } from 'msw'; -import { setupServer } from 'msw/node'; -import { registerMswTestHooks } from '../helpers'; -import { BitbucketIntegrationConfig } from './config'; -import { - getBitbucketDefaultBranch, - getBitbucketDownloadUrl, - getBitbucketFileFetchUrl, - getBitbucketRequestOptions, -} from './core'; - -describe('bitbucket core', () => { - const worker = setupServer(); - registerMswTestHooks(worker); - - describe('getBitbucketRequestOptions', () => { - it('inserts a token when needed', () => { - const withToken: BitbucketIntegrationConfig = { - host: '', - apiBaseUrl: '', - token: 'A', - }; - const withoutToken: BitbucketIntegrationConfig = { - host: '', - apiBaseUrl: '', - }; - expect( - (getBitbucketRequestOptions(withToken).headers as any).Authorization, - ).toEqual('Bearer A'); - expect( - (getBitbucketRequestOptions(withoutToken).headers as any).Authorization, - ).toBeUndefined(); - }); - - it('insert basic auth when needed', () => { - const withUsernameAndPassword: BitbucketIntegrationConfig = { - host: '', - apiBaseUrl: '', - username: 'some-user', - appPassword: 'my-secret', - }; - const withoutUsernameAndPassword: BitbucketIntegrationConfig = { - host: '', - apiBaseUrl: '', - }; - expect( - (getBitbucketRequestOptions(withUsernameAndPassword).headers as any) - .Authorization, - ).toEqual('Basic c29tZS11c2VyOm15LXNlY3JldA=='); - expect( - (getBitbucketRequestOptions(withoutUsernameAndPassword).headers as any) - .Authorization, - ).toBeUndefined(); - }); - }); - - describe('getBitbucketFileFetchUrl', () => { - it('rejects targets that do not look like URLs', () => { - const config: BitbucketIntegrationConfig = { host: '', apiBaseUrl: '' }; - expect(() => getBitbucketFileFetchUrl('a/b', config)).toThrow( - /Incorrect URL: a\/b/, - ); - }); - - it('happy path for Bitbucket Cloud', () => { - const config: BitbucketIntegrationConfig = { - host: 'bitbucket.org', - apiBaseUrl: 'https://api.bitbucket.org/2.0', - }; - expect( - getBitbucketFileFetchUrl( - 'https://bitbucket.org/org-name/repo-name/src/master/templates/my-template.yaml', - config, - ), - ).toEqual( - 'https://api.bitbucket.org/2.0/repositories/org-name/repo-name/src/master/templates/my-template.yaml', - ); - }); - - it('happy path for Bitbucket Server', () => { - const config: BitbucketIntegrationConfig = { - host: 'bitbucket.mycompany.net', - apiBaseUrl: 'https://bitbucket.mycompany.net/rest/api/1.0', - }; - expect( - getBitbucketFileFetchUrl( - 'https://bitbucket.mycompany.net/projects/a/repos/b/browse/path/to/c.yaml', - config, - ), - ).toEqual( - 'https://bitbucket.mycompany.net/rest/api/1.0/projects/a/repos/b/raw/path/to/c.yaml?at=', - ); - }); - }); - - describe('getBitbucketDownloadUrl', () => { - it('add path param if a path is specified for Bitbucket Server', async () => { - const defaultBranchResponse = { - displayId: 'main', - }; - worker.use( - rest.get( - 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/default-branch', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json(defaultBranchResponse), - ), - ), - ); - - const config: BitbucketIntegrationConfig = { - host: 'bitbucket.mycompany.net', - apiBaseUrl: 'https://api.bitbucket.mycompany.net/rest/api/1.0', - }; - const result = await getBitbucketDownloadUrl( - 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/docs', - config, - ); - expect(result).toEqual( - 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/archive?format=tgz&at=main&prefix=backstage-mock&path=docs', - ); - }); - - it('does not double encode the filepath', async () => { - const config: BitbucketIntegrationConfig = { - host: 'bitbucket.mycompany.net', - apiBaseUrl: 'https://api.bitbucket.mycompany.net/rest/api/1.0', - }; - const result = await getBitbucketDownloadUrl( - 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/%2Fdocs?at=some-branch', - config, - ); - expect(result).toEqual( - 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/archive?format=tgz&at=some-branch&prefix=backstage-mock&path=%2Fdocs', - ); - }); - - it('do not add path param if no path is specified for Bitbucket Server', async () => { - const defaultBranchResponse = { - displayId: 'main', - }; - worker.use( - rest.get( - 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/default-branch', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json(defaultBranchResponse), - ), - ), - ); - const config: BitbucketIntegrationConfig = { - host: 'bitbucket.mycompany.net', - apiBaseUrl: 'https://api.bitbucket.mycompany.net/rest/api/1.0', - }; - const result = await getBitbucketDownloadUrl( - 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse', - config, - ); - - expect(result).toEqual( - 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/archive?format=tgz&at=main&prefix=backstage-mock', - ); - }); - - it('get by branch for Bitbucket Server', async () => { - const config: BitbucketIntegrationConfig = { - host: 'bitbucket.mycompany.net', - apiBaseUrl: 'https://api.bitbucket.mycompany.net/rest/api/1.0', - }; - const result = await getBitbucketDownloadUrl( - 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/docs?at=some-branch', - config, - ); - expect(result).toEqual( - 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/archive?format=tgz&at=some-branch&prefix=backstage-mock&path=docs', - ); - }); - - it('do not add path param for Bitbucket Cloud', async () => { - const config: BitbucketIntegrationConfig = { - host: 'bitbucket.org', - apiBaseUrl: 'https://api.bitbucket.org/2.0', - }; - const result = await getBitbucketDownloadUrl( - 'https://bitbucket.org/backstage/mock/src/master', - config, - ); - expect(result).toEqual( - 'https://bitbucket.org/backstage/mock/get/master.tar.gz', - ); - }); - }); - - describe('getBitbucketDefaultBranch', () => { - it('return default branch for Bitbucket Cloud', async () => { - const repoInfoResponse = { - mainbranch: { - name: 'main', - }, - }; - worker.use( - rest.get( - 'https://api.bitbucket.org/2.0/repositories/backstage/mock', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json(repoInfoResponse), - ), - ), - ); - const config: BitbucketIntegrationConfig = { - host: 'bitbucket.org', - apiBaseUrl: 'https://api.bitbucket.org/2.0', - }; - const defaultBranch = await getBitbucketDefaultBranch( - 'https://bitbucket.org/backstage/mock/src/main', - config, - ); - expect(defaultBranch).toEqual('main'); - }); - - it('return default branch for Bitbucket Server', async () => { - const defaultBranchResponse = { - displayId: 'main', - }; - worker.use( - rest.get( - 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/default-branch', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json(defaultBranchResponse), - ), - ), - ); - const config: BitbucketIntegrationConfig = { - host: 'bitbucket.mycompany.net', - apiBaseUrl: 'https://api.bitbucket.mycompany.net/rest/api/1.0', - }; - const defaultBranch = await getBitbucketDefaultBranch( - 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/README.md', - config, - ); - expect(defaultBranch).toEqual('main'); - }); - - it('return default branch for Bitbucket Server for bitbucket version 5.11', async () => { - const defaultBranchResponse = { - displayId: 'main', - }; - worker.use( - rest.get( - 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/default-branch', - (_, res, ctx) => - res( - ctx.status(404), - ctx.set('Content-Type', 'application/json'), - ctx.json(defaultBranchResponse), - ), - ), - rest.get( - 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/branches/default', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json(defaultBranchResponse), - ), - ), - ); - const config: BitbucketIntegrationConfig = { - host: 'bitbucket.mycompany.net', - apiBaseUrl: 'https://api.bitbucket.mycompany.net/rest/api/1.0', - }; - const defaultBranch = await getBitbucketDefaultBranch( - 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/README.md', - config, - ); - expect(defaultBranch).toEqual('main'); - }); - }); -}); diff --git a/packages/integration/src/bitbucket/core.ts b/packages/integration/src/bitbucket/core.ts deleted file mode 100644 index 5c5533155d..0000000000 --- a/packages/integration/src/bitbucket/core.ts +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2020 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 fetch from 'cross-fetch'; -import parseGitUrl from 'git-url-parse'; -import { BitbucketIntegrationConfig } from './config'; - -/** - * Given a URL pointing to a path on a provider, returns the default branch. - * - * @param url - A URL pointing to a path - * @param config - The relevant provider config - * @public - * @deprecated no longer in use, bitbucket integration replaced by integrations bitbucketCloud and bitbucketServer. - */ -export async function getBitbucketDefaultBranch( - url: string, - config: BitbucketIntegrationConfig, -): Promise { - const { name: repoName, owner: project, resource } = parseGitUrl(url); - - const isHosted = resource === 'bitbucket.org'; - // Bitbucket Server https://docs.atlassian.com/bitbucket-server/rest/7.9.0/bitbucket-rest.html#idp184 - let branchUrl = isHosted - ? `${config.apiBaseUrl}/repositories/${project}/${repoName}` - : `${config.apiBaseUrl}/projects/${project}/repos/${repoName}/default-branch`; - - let response = await fetch(branchUrl, getBitbucketRequestOptions(config)); - - if (response.status === 404 && !isHosted) { - // First try the new format, and then if it gets specifically a 404 it should try the old format - // (to support old Atlassian Bitbucket v5.11.1 format ) - branchUrl = `${config.apiBaseUrl}/projects/${project}/repos/${repoName}/branches/default`; - response = await fetch(branchUrl, getBitbucketRequestOptions(config)); - } - - if (!response.ok) { - const message = `Failed to retrieve default branch from ${branchUrl}, ${response.status} ${response.statusText}`; - throw new Error(message); - } - - let defaultBranch; - if (isHosted) { - const repoInfo = await response.json(); - defaultBranch = repoInfo.mainbranch.name; - } else { - const { displayId } = await response.json(); - defaultBranch = displayId; - } - if (!defaultBranch) { - throw new Error( - `Failed to read default branch from ${branchUrl}. ` + - `Response ${response.status} ${response.json()}`, - ); - } - return defaultBranch; -} - -/** - * Given a URL pointing to a path on a provider, returns a URL that is suitable - * for downloading the subtree. - * - * @param url - A URL pointing to a path - * @param config - The relevant provider config - * @public - * @deprecated no longer in use, bitbucket integration replaced by integrations bitbucketCloud and bitbucketServer. - */ -export async function getBitbucketDownloadUrl( - url: string, - config: BitbucketIntegrationConfig, -): Promise { - const { - name: repoName, - owner: project, - ref, - protocol, - resource, - filepath, - } = parseGitUrl(url); - - const isHosted = resource === 'bitbucket.org'; - - let branch = ref; - if (!branch) { - branch = await getBitbucketDefaultBranch(url, config); - } - // path will limit the downloaded content - // /docs will only download the docs folder and everything below it - // /docs/index.md will download the docs folder and everything below it - const path = filepath - ? `&path=${encodeURIComponent(decodeURIComponent(filepath))}` - : ''; - const archiveUrl = isHosted - ? `${protocol}://${resource}/${project}/${repoName}/get/${branch}.tar.gz` - : `${config.apiBaseUrl}/projects/${project}/repos/${repoName}/archive?format=tgz&at=${branch}&prefix=${project}-${repoName}${path}`; - - return archiveUrl; -} - -/** - * Given a URL pointing to a file on a provider, returns a URL that is suitable - * for fetching the contents of the data. - * - * @remarks - * - * Converts - * from: https://bitbucket.org/orgname/reponame/src/master/file.yaml - * to: https://api.bitbucket.org/2.0/repositories/orgname/reponame/src/master/file.yaml - * - * @param url - A URL pointing to a file - * @param config - The relevant provider config - * @public - * @deprecated no longer in use, bitbucket integration replaced by integrations bitbucketCloud and bitbucketServer. - */ -export function getBitbucketFileFetchUrl( - url: string, - config: BitbucketIntegrationConfig, -): string { - try { - const { owner, name, ref, filepathtype, filepath } = parseGitUrl(url); - if ( - !owner || - !name || - (filepathtype !== 'browse' && - filepathtype !== 'raw' && - filepathtype !== 'src') - ) { - throw new Error('Invalid Bitbucket URL or file path'); - } - - const pathWithoutSlash = filepath.replace(/^\//, ''); - - if (config.host === 'bitbucket.org') { - if (!ref) { - throw new Error('Invalid Bitbucket URL or file path'); - } - return `${config.apiBaseUrl}/repositories/${owner}/${name}/src/${ref}/${pathWithoutSlash}`; - } - return `${config.apiBaseUrl}/projects/${owner}/repos/${name}/raw/${pathWithoutSlash}?at=${ref}`; - } catch (e) { - throw new Error(`Incorrect URL: ${url}, ${e}`); - } -} - -/** - * Gets the request options necessary to make requests to a given provider. - * - * @param config - The relevant provider config - * @public - * @deprecated no longer in use, bitbucket integration replaced by integrations bitbucketCloud and bitbucketServer. - */ -export function getBitbucketRequestOptions( - config: BitbucketIntegrationConfig, -): { headers: Record } { - const headers: Record = {}; - - if (config.token) { - headers.Authorization = `Bearer ${config.token}`; - } else if (config.username && config.appPassword) { - const buffer = Buffer.from( - `${config.username}:${config.appPassword}`, - 'utf8', - ); - headers.Authorization = `Basic ${buffer.toString('base64')}`; - } - - return { - headers, - }; -} diff --git a/packages/integration/src/bitbucket/index.ts b/packages/integration/src/bitbucket/index.ts deleted file mode 100644 index 356e760fd8..0000000000 --- a/packages/integration/src/bitbucket/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2020 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 { BitbucketIntegration } from './BitbucketIntegration'; -export { - readBitbucketIntegrationConfig, - readBitbucketIntegrationConfigs, -} from './config'; -export type { BitbucketIntegrationConfig } from './config'; -export { - getBitbucketDefaultBranch, - getBitbucketDownloadUrl, - getBitbucketFileFetchUrl, - getBitbucketRequestOptions, -} from './core'; diff --git a/packages/integration/src/gerrit/core.test.ts b/packages/integration/src/gerrit/core.test.ts index 5899196f65..60bd898008 100644 --- a/packages/integration/src/gerrit/core.test.ts +++ b/packages/integration/src/gerrit/core.test.ts @@ -20,7 +20,6 @@ import fetch from 'cross-fetch'; import { registerMswTestHooks } from '../helpers'; import { GerritIntegrationConfig } from './config'; import { - buildGerritGitilesArchiveUrl, buildGerritGitilesArchiveUrlFromLocation, buildGerritGitilesUrl, getGerritBranchApiUrl, @@ -28,7 +27,6 @@ import { getGerritRequestOptions, parseGerritJsonResponse, parseGitilesUrlRef, - parseGerritGitilesUrl, getGerritFileContentsApiUrl, } from './core'; @@ -36,86 +34,6 @@ describe('gerrit core', () => { const worker = setupServer(); registerMswTestHooks(worker); - describe('buildGerritGitilesArchiveUrl', () => { - const config: GerritIntegrationConfig = { - host: 'gerrit.com', - baseUrl: 'https://gerrit.com', - gitilesBaseUrl: 'https://gerrit.com/gitiles', - }; - const configWithPath: GerritIntegrationConfig = { - host: 'gerrit.com', - baseUrl: 'https://gerrit.com/gerrit', - gitilesBaseUrl: 'https://gerrit.com/gerrit/plugins/gitiles', - }; - const configWithDedicatedGitiles: GerritIntegrationConfig = { - host: 'gerrit.com', - baseUrl: 'https://gerrit.com/gerrit', - gitilesBaseUrl: 'https://dedicated-gitiles-server.com/gerrit/gitiles', - }; - it('can create an archive url for a branch', () => { - expect(buildGerritGitilesArchiveUrl(config, 'repo', 'dev', '')).toEqual( - 'https://gerrit.com/gitiles/repo/+archive/refs/heads/dev.tar.gz', - ); - - expect(buildGerritGitilesArchiveUrl(config, 'repo', 'dev', '/')).toEqual( - 'https://gerrit.com/gitiles/repo/+archive/refs/heads/dev.tar.gz', - ); - }); - it('can create an archive url for a specific directory', () => { - expect( - buildGerritGitilesArchiveUrl(config, 'repo', 'dev', 'docs'), - ).toEqual( - 'https://gerrit.com/gitiles/repo/+archive/refs/heads/dev/docs.tar.gz', - ); - }); - it('can create an authenticated url when auth is enabled', () => { - const authConfig = { - ...config, - username: 'username', - password: 'password', - }; - expect( - buildGerritGitilesArchiveUrl(authConfig, 'repo', 'dev', 'docs'), - ).toEqual( - 'https://gerrit.com/a/gitiles/repo/+archive/refs/heads/dev/docs.tar.gz', - ); - }); - it('can create an authenticated url when auth is enabled and an url-path is used', () => { - const authConfig = { - ...configWithPath, - username: 'username', - password: 'password', - }; - expect( - buildGerritGitilesArchiveUrl(authConfig, 'repo', 'dev', 'docs'), - ).toEqual( - 'https://gerrit.com/gerrit/a/plugins/gitiles/repo/+archive/refs/heads/dev/docs.tar.gz', - ); - }); - it('Cannot build an authenticated url when a dedicated Gitiles server is used', () => { - const authConfig = { - ...configWithDedicatedGitiles, - username: 'username', - password: 'password', - }; - expect(() => - buildGerritGitilesArchiveUrl(authConfig, 'repo', 'dev', 'docs'), - ).toThrow( - 'Since the baseUrl (Gerrit) is not part of the gitilesBaseUrl, an authentication URL could not be constructed.', - ); - }); - it('Build a non-authenticated url when a dedicated Gitiles server is used', () => { - const authConfig = { - ...configWithDedicatedGitiles, - }; - expect( - buildGerritGitilesArchiveUrl(authConfig, 'repo', 'dev', 'docs'), - ).toEqual( - 'https://dedicated-gitiles-server.com/gerrit/gitiles/repo/+archive/refs/heads/dev/docs.tar.gz', - ); - }); - }); - describe('buildGerritGitilesArchiveUrlFromLocation', () => { const config: GerritIntegrationConfig = { host: 'gerrit.com', @@ -238,6 +156,7 @@ describe('gerrit core', () => { ).toBeUndefined(); }); }); + describe('parseGitilesUrlRef', () => { const config: GerritIntegrationConfig = { host: 'gerrit.com', @@ -360,64 +279,6 @@ describe('gerrit core', () => { }); }); }); - describe('parseGerritGitilesUrl', () => { - it('can parse a valid gitiles urls.', () => { - const config: GerritIntegrationConfig = { - host: 'gerrit.com', - gitilesBaseUrl: 'https://gerrit.com/gitiles', - }; - const { branch, filePath, project } = parseGerritGitilesUrl( - config, - 'https://gerrit.com/gitiles/web/project/+/refs/heads/master/README.md', - ); - expect(project).toEqual('web/project'); - expect(branch).toEqual('master'); - expect(filePath).toEqual('README.md'); - - const { filePath: rootPath } = parseGerritGitilesUrl( - config, - 'https://gerrit.com/gitiles/web/project/+/refs/heads/master', - ); - expect(rootPath).toEqual('/'); - }); - it('can parse a valid authenticated gitiles url.', () => { - const config: GerritIntegrationConfig = { - host: 'gerrit.com', - gitilesBaseUrl: 'https://gerrit.com/gitiles', - }; - const { branch, filePath, project } = parseGerritGitilesUrl( - config, - 'https://gerrit.com/a/gitiles/web/project/+/refs/heads/master/README.md', - ); - expect(project).toEqual('web/project'); - expect(branch).toEqual('master'); - expect(filePath).toEqual('README.md'); - - const { filePath: rootPath } = parseGerritGitilesUrl( - config, - 'https://gerrit.com/gitiles/web/project/+/refs/heads/master', - ); - expect(rootPath).toEqual('/'); - }); - it('throws on incorrect gitiles urls.', () => { - const config: GerritIntegrationConfig = { - host: 'gerrit.com', - gitilesBaseUrl: 'https://gerrit.com', - }; - expect(() => - parseGerritGitilesUrl( - config, - 'https://gerrit.com/+/refs/heads/master/README.md', - ), - ).toThrow(/project/); - expect(() => - parseGerritGitilesUrl( - config, - 'https://gerrit.com/web/project/+/refs/changes/1/11/master/README.md', - ), - ).toThrow(/branch/); - }); - }); describe('getGerritBranchApiUrl', () => { it('can create an url for anonymous access.', () => { diff --git a/packages/integration/src/gerrit/core.ts b/packages/integration/src/gerrit/core.ts index ae6a5baa10..e8db1305ed 100644 --- a/packages/integration/src/gerrit/core.ts +++ b/packages/integration/src/gerrit/core.ts @@ -18,70 +18,6 @@ import { GerritIntegrationConfig } from './config'; const GERRIT_BODY_PREFIX = ")]}'"; -/** - * Parse a Gitiles URL and return branch, file path and project. - * - * @remarks - * - * Gerrit only handles code reviews so it does not have a native way to browse - * or showing the content of gits. Image if Github only had the "pull requests" - * tab. - * - * Any source code browsing is instead handled by optional services outside - * Gerrit. The url format chosen for the Gerrit url reader is the one used by - * the Gitiles project. Gerrit will work perfectly with Backstage without - * having Gitiles installed but there are some places in the Backstage GUI - * with links to the url used by the url reader. These will not work unless - * the urls point to an actual Gitiles installation. - * - * Gitiles url: - * https://g.com/optional_path/\{project\}/+/refs/heads/\{branch\}/\{filePath\} - * https://g.com/a/optional_path/\{project\}/+/refs/heads/\{branch\}/\{filePath\} - * - * - * @param url - An URL pointing to a file stored in git. - * @public - * @deprecated `parseGerritGitilesUrl` is deprecated. Use - * {@link parseGitilesUrlRef} instead. - */ -export function parseGerritGitilesUrl( - config: GerritIntegrationConfig, - url: string, -): { branch: string; filePath: string; project: string } { - const baseUrlParse = new URL(config.gitilesBaseUrl!); - const urlParse = new URL(url); - - // Remove the gerrit authentication prefix '/a/' from the url - // In case of the gitilesBaseUrl is https://review.gerrit.com/plugins/gitiles - // and the url provided is https://review.gerrit.com/a/plugins/gitiles/... - // remove the prefix only if the pathname start with '/a/' - const urlPath = urlParse.pathname - .substring(urlParse.pathname.startsWith('/a/') ? 2 : 0) - .replace(baseUrlParse.pathname, ''); - - const parts = urlPath.split('/').filter(p => !!p); - - const projectEndIndex = parts.indexOf('+'); - - if (projectEndIndex <= 0) { - throw new Error(`Unable to parse project from url: ${url}`); - } - const project = trimStart(parts.slice(0, projectEndIndex).join('/'), '/'); - - const branchIndex = parts.indexOf('heads'); - if (branchIndex <= 0) { - throw new Error(`Unable to parse branch from url: ${url}`); - } - const branch = parts[branchIndex + 1]; - const filePath = parts.slice(branchIndex + 2).join('/'); - - return { - branch, - filePath: filePath === '' ? '/' : filePath, - project, - }; -} - /** * Parses Gitiles urls and returns the following: * @@ -231,30 +167,6 @@ export function buildGerritEditUrl( )}`; } -/** - * Build a Gerrit Gitiles archive url that targets a specific branch and path - * - * @param config - A Gerrit provider config. - * @param project - The name of the git project - * @param branch - The branch we will target. - * @param filePath - The absolute file path. - * @public - * @deprecated `buildGerritGitilesArchiveUrl` is deprecated. Use - * {@link buildGerritGitilesArchiveUrlFromLocation} instead. - */ -export function buildGerritGitilesArchiveUrl( - config: GerritIntegrationConfig, - project: string, - branch: string, - filePath: string, -): string { - const archiveName = - filePath === '/' || filePath === '' ? '.tar.gz' : `/${filePath}.tar.gz`; - return `${getGitilesAuthenticationUrl( - config, - )}/${project}/+archive/refs/heads/${branch}${archiveName}`; -} - /** * Build a Gerrit Gitiles archive url from a Gitiles url. * @@ -350,11 +262,15 @@ export function getGerritBranchApiUrl( config: GerritIntegrationConfig, url: string, ) { - const { branch, project } = parseGerritGitilesUrl(config, url); + const { ref, refType, project } = parseGitilesUrlRef(config, url); + + if (refType !== 'branch') { + throw new Error(`Unsupported gitiles ref type: ${refType}`); + } return `${config.baseUrl}${getAuthenticationPrefix( config, - )}projects/${encodeURIComponent(project)}/branches/${branch}`; + )}projects/${encodeURIComponent(project)}/branches/${ref}`; } /** @@ -367,7 +283,7 @@ export function getGerritCloneRepoUrl( config: GerritIntegrationConfig, url: string, ) { - const { project } = parseGerritGitilesUrl(config, url); + const { project } = parseGitilesUrlRef(config, url); return `${config.cloneUrl}${getAuthenticationPrefix(config)}${project}`; } diff --git a/packages/integration/src/gerrit/index.ts b/packages/integration/src/gerrit/index.ts index 534315cfba..5af75e36ab 100644 --- a/packages/integration/src/gerrit/index.ts +++ b/packages/integration/src/gerrit/index.ts @@ -19,7 +19,6 @@ export { readGerritIntegrationConfigs, } from './config'; export { - buildGerritGitilesArchiveUrl, buildGerritGitilesArchiveUrlFromLocation, getGitilesAuthenticationUrl, getGerritBranchApiUrl, @@ -28,7 +27,6 @@ export { getGerritProjectsApiUrl, getGerritRequestOptions, parseGerritJsonResponse, - parseGerritGitilesUrl, parseGitilesUrlRef, } from './core'; diff --git a/packages/integration/src/github/core.test.ts b/packages/integration/src/github/core.test.ts index 982a096b43..8e9507b239 100644 --- a/packages/integration/src/github/core.test.ts +++ b/packages/integration/src/github/core.test.ts @@ -15,7 +15,7 @@ */ import { GithubIntegrationConfig } from './config'; -import { getGithubFileFetchUrl, getGitHubRequestOptions } from './core'; +import { getGithubFileFetchUrl } from './core'; import { GithubCredentials } from './types'; describe('github core', () => { @@ -35,28 +35,6 @@ describe('github core', () => { type: 'token', }; - describe('getGitHubRequestOptions', () => { - it('inserts a token when needed', () => { - const withToken: GithubIntegrationConfig = { - host: '', - rawBaseUrl: '', - token: 'A', - }; - const withoutToken: GithubIntegrationConfig = { - host: '', - rawBaseUrl: '', - }; - expect( - (getGitHubRequestOptions(withToken, appCredentials).headers as any) - .Authorization, - ).toEqual('token A'); - expect( - (getGitHubRequestOptions(withoutToken, noCredentials).headers as any) - .Authorization, - ).toBeUndefined(); - }); - }); - describe('getGithubFileFetchUrl', () => { it('rejects targets that do not look like URLs', () => { const config: GithubIntegrationConfig = { host: '', apiBaseUrl: '' }; diff --git a/packages/integration/src/github/core.ts b/packages/integration/src/github/core.ts index a755f3383c..d1f583b2fb 100644 --- a/packages/integration/src/github/core.ts +++ b/packages/integration/src/github/core.ts @@ -63,30 +63,6 @@ export function getGithubFileFetchUrl( } } -/** - * Gets the request options necessary to make requests to a given provider. - * - * @deprecated This function is no longer used internally - * @param config - The relevant provider config - * @public - */ -export function getGitHubRequestOptions( - config: GithubIntegrationConfig, - credentials: GithubCredentials, -): { headers: Record } { - const headers: Record = {}; - - if (chooseEndpoint(config, credentials) === 'api') { - headers.Accept = 'application/vnd.github.v3.raw'; - } - - if (credentials.token) { - headers.Authorization = `token ${credentials.token}`; - } - - return { headers }; -} - export function chooseEndpoint( config: GithubIntegrationConfig, credentials: GithubCredentials, diff --git a/packages/integration/src/github/index.ts b/packages/integration/src/github/index.ts index ea6f18f2e0..450ac38a63 100644 --- a/packages/integration/src/github/index.ts +++ b/packages/integration/src/github/index.ts @@ -19,7 +19,7 @@ export { readGithubIntegrationConfigs, } from './config'; export type { GithubAppConfig, GithubIntegrationConfig } from './config'; -export { getGithubFileFetchUrl, getGitHubRequestOptions } from './core'; +export { getGithubFileFetchUrl } from './core'; export { DefaultGithubCredentialsProvider } from './DefaultGithubCredentialsProvider'; export { GithubAppCredentialsMux, diff --git a/packages/integration/src/index.ts b/packages/integration/src/index.ts index a625f5d83c..1c0919fcbd 100644 --- a/packages/integration/src/index.ts +++ b/packages/integration/src/index.ts @@ -24,7 +24,6 @@ export * from './awsS3'; export * from './awsCodeCommit'; export * from './azureBlobStorage'; export * from './azure'; -export * from './bitbucket'; export * from './bitbucketCloud'; export * from './bitbucketServer'; export * from './gerrit'; diff --git a/packages/integration/src/registry.ts b/packages/integration/src/registry.ts index 2e8111903b..6aca5b197b 100644 --- a/packages/integration/src/registry.ts +++ b/packages/integration/src/registry.ts @@ -19,7 +19,6 @@ import { AwsS3Integration } from './awsS3/AwsS3Integration'; import { AwsCodeCommitIntegration } from './awsCodeCommit'; import { AzureIntegration } from './azure/AzureIntegration'; import { BitbucketCloudIntegration } from './bitbucketCloud/BitbucketCloudIntegration'; -import { BitbucketIntegration } from './bitbucket/BitbucketIntegration'; import { BitbucketServerIntegration } from './bitbucketServer/BitbucketServerIntegration'; import { GerritIntegration } from './gerrit/GerritIntegration'; import { GithubIntegration } from './github/GithubIntegration'; @@ -39,10 +38,6 @@ export interface ScmIntegrationRegistry awsCodeCommit: ScmIntegrationsGroup; azureBlobStorage: ScmIntegrationsGroup; azure: ScmIntegrationsGroup; - /** - * @deprecated in favor of `bitbucketCloud` and `bitbucketServer` - */ - bitbucket: ScmIntegrationsGroup; bitbucketCloud: ScmIntegrationsGroup; bitbucketServer: ScmIntegrationsGroup; gerrit: ScmIntegrationsGroup; diff --git a/plugins/scaffolder-backend-module-bitbucket/.eslintrc.js b/plugins/scaffolder-backend-module-bitbucket/.eslintrc.js deleted file mode 100644 index e2a53a6ad2..0000000000 --- a/plugins/scaffolder-backend-module-bitbucket/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md deleted file mode 100644 index 286dcbece7..0000000000 --- a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md +++ /dev/null @@ -1,1341 +0,0 @@ -# @backstage/plugin-scaffolder-backend-module-bitbucket - -## 0.3.20-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/integration@1.21.0-next.0 - - @backstage/backend-plugin-api@1.7.1-next.0 - - @backstage/config@1.3.6 - - @backstage/errors@1.2.7 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.3.4-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.19-next.0 - - @backstage/plugin-scaffolder-node@0.12.6-next.0 - -## 0.3.19 - -### Patch Changes - -- 7455dae: Use node prefix on native imports -- Updated dependencies - - @backstage/integration@1.20.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.3.3 - - @backstage/backend-plugin-api@1.7.0 - - @backstage/plugin-scaffolder-node@0.12.5 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.18 - -## 0.3.19-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/integration@1.20.0-next.1 - - @backstage/backend-plugin-api@1.7.0-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.3.3-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.18-next.1 - - @backstage/plugin-scaffolder-node@0.12.5-next.1 - -## 0.3.19-next.0 - -### Patch Changes - -- 7455dae: Use node prefix on native imports -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.3.2-next.0 - - @backstage/backend-plugin-api@1.7.0-next.0 - - @backstage/plugin-scaffolder-node@0.12.4-next.0 - - @backstage/integration@1.19.3-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.18-next.0 - - @backstage/config@1.3.6 - - @backstage/errors@1.2.7 - -## 0.3.18 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@1.6.1 - - @backstage/plugin-scaffolder-node@0.12.3 - - @backstage/integration@1.19.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.3.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.17 - -## 0.3.18-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/integration@1.19.2-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.3.1-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.17-next.0 - - @backstage/plugin-scaffolder-node@0.12.3-next.0 - -## 0.3.17 - -### Patch Changes - -- Updated dependencies - - @backstage/integration@1.19.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.3.0 - - @backstage/backend-plugin-api@1.6.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.16 - - @backstage/plugin-scaffolder-node@0.12.2 - -## 0.3.17-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/integration@1.18.3-next.1 - - @backstage/backend-plugin-api@1.6.0-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.16-next.1 - - @backstage/config@1.3.6 - - @backstage/errors@1.2.7 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.16-next.1 - - @backstage/plugin-scaffolder-node@0.12.2-next.1 - -## 0.3.17-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@1.5.1-next.0 - - @backstage/integration@1.18.3-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.16-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.16-next.0 - - @backstage/plugin-scaffolder-node@0.12.2-next.0 - - @backstage/config@1.3.6 - - @backstage/errors@1.2.7 - -## 0.3.16 - -### Patch Changes - -- fa255f5: Support for Bitbucket Cloud's API token was added as `appPassword` is deprecated (no new creation from September 9, 2025) and will be removed on June 9, 2026. - - API token usage example: - - ```yaml - integrations: - bitbucketCloud: - - username: user@domain.com - token: my-token - ``` - -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.15 - - @backstage/integration@1.18.2 - - @backstage/backend-plugin-api@1.5.0 - - @backstage/config@1.3.6 - - @backstage/plugin-scaffolder-node@0.12.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.15 - -## 0.3.16-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@1.5.0-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.15-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.15-next.1 - - @backstage/plugin-scaffolder-node@0.12.1-next.1 - -## 0.3.16-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/config@1.3.6-next.0 - - @backstage/integration@1.18.2-next.0 - - @backstage/plugin-scaffolder-node@0.12.1-next.0 - - @backstage/backend-plugin-api@1.4.5-next.0 - - @backstage/errors@1.2.7 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.15-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.15-next.0 - -## 0.3.15 - -### Patch Changes - -- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export -- Updated dependencies - - @backstage/integration@1.18.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14 - - @backstage/plugin-scaffolder-node@0.12.0 - - @backstage/config@1.3.5 - - @backstage/backend-plugin-api@1.4.4 - -## 0.3.15-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/config@1.3.4-next.0 - - @backstage/integration@1.18.1-next.1 - - @backstage/backend-plugin-api@1.4.4-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14-next.1 - - @backstage/plugin-scaffolder-node@0.12.0-next.1 - -## 0.3.15-next.0 - -### Patch Changes - -- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export -- Updated dependencies - - @backstage/integration@1.18.1-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14-next.0 - - @backstage/plugin-scaffolder-node@0.12.0-next.0 - - @backstage/backend-plugin-api@1.4.3 - - @backstage/config@1.3.3 - - @backstage/errors@1.2.7 - -## 0.3.14 - -### Patch Changes - -- Updated dependencies - - @backstage/integration@1.18.0 - - @backstage/backend-plugin-api@1.4.3 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.13 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.13 - - @backstage/plugin-scaffolder-node@0.11.1 - -## 0.3.14-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/integration@1.18.0-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.13-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.13-next.0 - - @backstage/plugin-scaffolder-node@0.11.1-next.0 - - @backstage/backend-plugin-api@1.4.3-next.0 - -## 0.3.13 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.11.0 - - @backstage/backend-plugin-api@1.4.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.12 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.12 - -## 0.3.13-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.11.0-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.12-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.12-next.0 - - @backstage/backend-plugin-api@1.4.2-next.0 - - @backstage/config@1.3.3 - - @backstage/errors@1.2.7 - - @backstage/integration@1.17.1 - -## 0.3.12 - -### Patch Changes - -- Updated dependencies - - @backstage/config@1.3.3 - - @backstage/plugin-scaffolder-node@0.10.0 - - @backstage/integration@1.17.1 - - @backstage/backend-plugin-api@1.4.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.11 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.11 - -## 0.3.12-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.10.0-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.11-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.11-next.2 - -## 0.3.12-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/config@1.3.3-next.0 - - @backstage/integration@1.17.1-next.1 - - @backstage/backend-plugin-api@1.4.1-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.11-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.11-next.1 - - @backstage/plugin-scaffolder-node@0.9.1-next.1 - -## 0.3.12-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.9.1-next.0 - - @backstage/integration@1.17.1-next.0 - - @backstage/backend-plugin-api@1.4.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.11-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.11-next.0 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - -## 0.3.11 - -### Patch Changes - -- 7f710d2: Migrating `bitbucket` actions to use the new `zod` format -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.10 - - @backstage/plugin-scaffolder-node@0.9.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.10 - - @backstage/backend-plugin-api@1.4.0 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - - @backstage/integration@1.17.0 - -## 0.3.11-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.9.0-next.2 - - @backstage/backend-plugin-api@1.4.0-next.1 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - - @backstage/integration@1.17.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.10-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.10-next.2 - -## 0.3.11-next.1 - -### Patch Changes - -- 7f710d2: Migrating `bitbucket` actions to use the new `zod` format -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.10-next.1 - - @backstage/plugin-scaffolder-node@0.8.3-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.10-next.1 - - @backstage/backend-plugin-api@1.4.0-next.1 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - - @backstage/integration@1.17.0 - -## 0.3.11-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.10-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.10-next.0 - - @backstage/plugin-scaffolder-node@0.8.3-next.0 - - @backstage/backend-plugin-api@1.4.0-next.0 - -## 0.3.10 - -### Patch Changes - -- 72d019d: Removed various typos -- Updated dependencies - - @backstage/integration@1.17.0 - - @backstage/backend-plugin-api@1.3.1 - - @backstage/plugin-scaffolder-node@0.8.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.9 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.9 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - -## 0.3.10-next.3 - -### Patch Changes - -- Updated dependencies - - @backstage/integration@1.17.0-next.3 - - @backstage/plugin-scaffolder-node@0.8.2-next.3 - - @backstage/backend-plugin-api@1.3.1-next.2 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.9-next.3 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.9-next.3 - -## 0.3.10-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/integration@1.17.0-next.2 - - @backstage/config@1.3.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.9-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.9-next.2 - - @backstage/plugin-scaffolder-node@0.8.2-next.2 - - @backstage/backend-plugin-api@1.3.1-next.1 - - @backstage/errors@1.2.7 - -## 0.3.10-next.1 - -### Patch Changes - -- 72d019d: Removed various typos -- Updated dependencies - - @backstage/backend-plugin-api@1.3.1-next.1 - - @backstage/integration@1.16.4-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.9-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.9-next.1 - - @backstage/plugin-scaffolder-node@0.8.2-next.1 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - -## 0.3.10-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/integration@1.16.4-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.9-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.9-next.0 - - @backstage/plugin-scaffolder-node@0.8.2-next.0 - - @backstage/backend-plugin-api@1.3.1-next.0 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - -## 0.3.9 - -### Patch Changes - -- adfceee: Made "publish:bitbucket" action idempotent -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.8.1 - - @backstage/backend-plugin-api@1.3.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.8 - - @backstage/integration@1.16.3 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.8 - -## 0.3.9-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/integration@1.16.3-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.8-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.8-next.1 - - @backstage/plugin-scaffolder-node@0.8.1-next.1 - - @backstage/backend-plugin-api@1.2.1 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - -## 0.3.9-next.0 - -### Patch Changes - -- adfceee: Made "publish:bitbucket" action idempotent -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.8.1-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.8-next.0 - - @backstage/backend-plugin-api@1.2.1 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - - @backstage/integration@1.16.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.8-next.0 - -## 0.3.8 - -### Patch Changes - -- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder -- Updated dependencies - - @backstage/integration@1.16.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.7 - - @backstage/plugin-scaffolder-node@0.8.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.7 - - @backstage/backend-plugin-api@1.2.1 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - -## 0.3.8-next.2 - -### Patch Changes - -- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.7-next.2 - - @backstage/plugin-scaffolder-node@0.8.0-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.7-next.2 - - @backstage/integration@1.16.2-next.0 - - @backstage/backend-plugin-api@1.2.1-next.1 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - -## 0.3.8-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.7.1-next.1 - - @backstage/backend-plugin-api@1.2.1-next.1 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - - @backstage/integration@1.16.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.7-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.7-next.1 - -## 0.3.8-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@1.2.1-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.7-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.7-next.0 - - @backstage/plugin-scaffolder-node@0.7.1-next.0 - -## 0.3.7 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@1.2.0 - - @backstage/plugin-scaffolder-node@0.7.0 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - - @backstage/integration@1.16.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.6 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.6 - -## 0.3.7-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.7.0-next.2 - - @backstage/backend-plugin-api@1.2.0-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.6-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.6-next.2 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - - @backstage/integration@1.16.1 - -## 0.3.7-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@1.2.0-next.1 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - - @backstage/integration@1.16.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.6-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.6-next.1 - - @backstage/plugin-scaffolder-node@0.7.0-next.1 - -## 0.3.7-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@1.2.0-next.0 - - @backstage/plugin-scaffolder-node@0.7.0-next.0 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - - @backstage/integration@1.16.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.6-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.6-next.0 - -## 0.3.6 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.6.3 - - @backstage/integration@1.16.1 - - @backstage/backend-plugin-api@1.1.1 - - @backstage/config@1.3.2 - - @backstage/errors@1.2.7 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.5 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.5 - -## 0.3.6-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@1.1.1-next.1 - - @backstage/config@1.3.2-next.0 - - @backstage/errors@1.2.7-next.0 - - @backstage/plugin-scaffolder-node@0.6.3-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.5-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.5-next.1 - - @backstage/integration@1.16.1-next.0 - -## 0.3.6-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.6.3-next.0 - - @backstage/backend-plugin-api@1.1.1-next.0 - - @backstage/config@1.3.1 - - @backstage/errors@1.2.6 - - @backstage/integration@1.16.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.5-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.5-next.0 - -## 0.3.5 - -### Patch Changes - -- 5f04976: Fixed a bug that caused missing code in published packages. -- 5c9cc05: Use native fetch instead of node-fetch -- Updated dependencies - - @backstage/integration@1.16.0 - - @backstage/backend-plugin-api@1.1.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.4 - - @backstage/plugin-scaffolder-node@0.6.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.4 - - @backstage/errors@1.2.6 - - @backstage/config@1.3.1 - -## 0.3.5-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@1.1.0-next.2 - - @backstage/errors@1.2.6-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.4-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.4-next.2 - - @backstage/plugin-scaffolder-node@0.6.2-next.2 - - @backstage/config@1.3.1-next.0 - - @backstage/integration@1.16.0-next.1 - -## 0.3.5-next.1 - -### Patch Changes - -- 5c9cc05: Use native fetch instead of node-fetch -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.6.2-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.4-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.4-next.1 - - @backstage/backend-plugin-api@1.1.0-next.1 - - @backstage/config@1.3.0 - - @backstage/errors@1.2.5 - - @backstage/integration@1.16.0-next.0 - -## 0.3.4-next.0 - -### Patch Changes - -- 5f04976: Fixed a bug that caused missing code in published packages. -- Updated dependencies - - @backstage/integration@1.16.0-next.0 - - @backstage/backend-plugin-api@1.0.3-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.3-next.0 - - @backstage/plugin-scaffolder-node@0.6.1-next.0 - - @backstage/config@1.3.0 - - @backstage/errors@1.2.5 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.3-next.0 - -## 0.3.2 - -### Patch Changes - -- Updated dependencies - - @backstage/config@1.3.0 - - @backstage/backend-plugin-api@1.0.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.2 - - @backstage/plugin-scaffolder-node@0.6.0 - - @backstage/errors@1.2.5 - - @backstage/integration@1.15.2 - -## 0.3.2-next.3 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@1.0.2-next.2 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/integration@1.15.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.2-next.3 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.2-next.3 - - @backstage/plugin-scaffolder-node@0.5.1-next.3 - -## 0.3.2-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@1.0.2-next.2 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/integration@1.15.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.2-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.2-next.2 - - @backstage/plugin-scaffolder-node@0.5.1-next.2 - -## 0.3.2-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.2-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.2-next.1 - - @backstage/backend-plugin-api@1.0.2-next.1 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/integration@1.15.1 - - @backstage/plugin-scaffolder-node@0.5.1-next.1 - -## 0.3.2-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@1.0.2-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/integration@1.15.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.2-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.2-next.0 - - @backstage/plugin-scaffolder-node@0.5.1-next.0 - -## 0.3.1 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.5.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.1 - - @backstage/integration@1.15.1 - - @backstage/backend-plugin-api@1.0.1 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.1 - -## 0.3.1-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.1-next.2 - - @backstage/integration@1.15.1-next.1 - - @backstage/plugin-scaffolder-node@0.5.0-next.2 - - @backstage/backend-plugin-api@1.0.1-next.1 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.1-next.2 - -## 0.3.1-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/integration@1.15.1-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.1-next.1 - - @backstage/backend-plugin-api@1.0.1-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.1-next.1 - - @backstage/plugin-scaffolder-node@0.5.0-next.1 - -## 0.3.1-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.5.0-next.0 - - @backstage/backend-plugin-api@1.0.1-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/integration@1.15.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.1-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.1-next.0 - -## 0.3.0 - -### Minor Changes - -- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. - - This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. - - As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@1.0.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.0 - - @backstage/integration@1.15.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/plugin-scaffolder-node@0.4.11 - -## 0.3.0-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.0-next.2 - - @backstage/backend-plugin-api@1.0.0-next.2 - - @backstage/integration@1.15.0-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.0-next.2 - - @backstage/plugin-scaffolder-node@0.4.11-next.2 - -## 0.3.0-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@0.9.0-next.1 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/integration@1.14.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.0-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.0-next.1 - - @backstage/plugin-scaffolder-node@0.4.11-next.1 - -## 0.3.0-next.0 - -### Minor Changes - -- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. - - This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. - - As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@0.9.0-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.0-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.0-next.0 - - @backstage/plugin-scaffolder-node@0.4.11-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/integration@1.14.0 - -## 0.2.13 - -### Patch Changes - -- 93095ee: Make sure node-fetch is version 2.7.0 or greater -- Updated dependencies - - @backstage/backend-plugin-api@0.8.0 - - @backstage/plugin-scaffolder-node@0.4.9 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.13 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.13 - - @backstage/integration@1.14.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - -## 0.2.13-next.3 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.13-next.3 - - @backstage/backend-plugin-api@0.8.0-next.3 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/integration@1.14.0-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.13-next.3 - - @backstage/plugin-scaffolder-node@0.4.9-next.3 - -## 0.2.13-next.2 - -### Patch Changes - -- 93095ee: Make sure node-fetch is version 2.7.0 or greater -- Updated dependencies - - @backstage/backend-plugin-api@0.8.0-next.2 - - @backstage/plugin-scaffolder-node@0.4.9-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.13-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.13-next.2 - - @backstage/integration@1.14.0-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - -## 0.2.13-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.13-next.1 - - @backstage/backend-plugin-api@0.7.1-next.1 - - @backstage/integration@1.14.0-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.13-next.1 - - @backstage/plugin-scaffolder-node@0.4.9-next.1 - -## 0.2.13-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/integration@1.14.0-next.0 - - @backstage/backend-plugin-api@0.7.1-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.13-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.13-next.0 - - @backstage/plugin-scaffolder-node@0.4.9-next.0 - -## 0.2.12 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@0.7.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.12 - - @backstage/integration@1.13.0 - - @backstage/plugin-scaffolder-node@0.4.8 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.12 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - -## 0.2.12-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@0.6.22-next.1 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/integration@1.13.0-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.12-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.12-next.1 - - @backstage/plugin-scaffolder-node@0.4.8-next.1 - -## 0.2.11-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@0.6.21-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.11-next.0 - - @backstage/integration@1.13.0-next.0 - - @backstage/plugin-scaffolder-node@0.4.7-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.11-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - -## 0.2.9 - -### Patch Changes - -- 78a0b08: Internal refactor to handle `BackendFeature` contract change. -- d44a20a: Added additional plugin metadata to `package.json`. -- Updated dependencies - - @backstage/backend-plugin-api@0.6.19 - - @backstage/integration@1.12.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.9 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.9 - - @backstage/plugin-scaffolder-node@0.4.5 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - -## 0.2.9-next.2 - -### Patch Changes - -- d44a20a: Added additional plugin metadata to `package.json`. -- Updated dependencies - - @backstage/backend-plugin-api@0.6.19-next.3 - - @backstage/integration@1.12.0-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.9-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.9-next.2 - - @backstage/plugin-scaffolder-node@0.4.5-next.3 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - -## 0.2.9-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@0.6.19-next.2 - - @backstage/integration@1.12.0-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.9-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.9-next.1 - - @backstage/plugin-scaffolder-node@0.4.5-next.2 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - -## 0.2.9-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@0.6.19-next.0 - - @backstage/plugin-scaffolder-node@0.4.5-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.9-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.9-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/integration@1.11.0 - -## 0.2.8 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@0.6.18 - - @backstage/plugin-scaffolder-node@0.4.4 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.8 - - @backstage/integration@1.11.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.8 - -## 0.2.8-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.4.4-next.2 - - @backstage/integration@1.11.0-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.8-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.8-next.2 - -## 0.2.8-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.4.4-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.8-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.8-next.1 - - @backstage/backend-plugin-api@0.6.18-next.1 - -## 0.2.8-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.8-next.0 - - @backstage/backend-plugin-api@0.6.18-next.0 - - @backstage/plugin-scaffolder-node@0.4.4-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/integration@1.10.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.8-next.0 - -## 0.2.7 - -### Patch Changes - -- 33f958a: Improve examples to ensure consistency across all publish actions -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.7 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.7 - - @backstage/backend-plugin-api@0.6.17 - - @backstage/integration@1.10.0 - - @backstage/plugin-scaffolder-node@0.4.3 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - -## 0.2.7-next.1 - -### Patch Changes - -- 33f958a: Improve examples to ensure consistency across all publish actions -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.7-next.1 - - @backstage/backend-plugin-api@0.6.17-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.7-next.1 - - @backstage/plugin-scaffolder-node@0.4.3-next.1 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/integration@1.10.0-next.0 - -## 0.2.7-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.7-next.0 - - @backstage/integration@1.10.0-next.0 - - @backstage/backend-plugin-api@0.6.17-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.7-next.0 - - @backstage/plugin-scaffolder-node@0.4.3-next.0 - -## 0.2.6 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@0.6.16 - - @backstage/plugin-scaffolder-node@0.4.2 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/integration@1.9.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.6 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.6 - -## 0.2.5 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.4.1 - - @backstage/backend-plugin-api@0.6.15 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/integration@1.9.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.5 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.5 - -## 0.2.4 - -### Patch Changes - -- 2bd1410: Removed unused dependencies -- f44589d: Introduced `createMockActionContext` to unify the way of creating scaffolder mock context. - - It will help to maintain tests in a long run during structural changes of action context. - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.4.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.4 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.4 - - @backstage/integration@1.9.1 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/backend-plugin-api@0.6.14 - -## 0.2.4-next.2 - -### Patch Changes - -- 2bd1410: Removed unused dependencies -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.4.0-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.4-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.4-next.2 - - @backstage/integration@1.9.1-next.2 - - @backstage/backend-plugin-api@0.6.14-next.2 - - @backstage/config@1.2.0-next.1 - - @backstage/errors@1.2.4-next.0 - -## 0.2.4-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/config@1.2.0-next.1 - - @backstage/plugin-scaffolder-node@0.4.0-next.1 - - @backstage/backend-common@0.21.4-next.1 - - @backstage/backend-plugin-api@0.6.14-next.1 - - @backstage/integration@1.9.1-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.4-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.4-next.1 - - @backstage/errors@1.2.4-next.0 - -## 0.2.3-next.0 - -### Patch Changes - -- f44589d: Introduced `createMockActionContext` to unify the way of creating scaffolder mock context. - - It will help to maintain tests in a long run during structural changes of action context. - -- Updated dependencies - - @backstage/backend-common@0.21.3-next.0 - - @backstage/errors@1.2.4-next.0 - - @backstage/plugin-scaffolder-node@0.3.3-next.0 - - @backstage/backend-plugin-api@0.6.13-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.3-next.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.3-next.0 - - @backstage/config@1.1.2-next.0 - - @backstage/integration@1.9.1-next.0 - -## 0.2.0 - -### Minor Changes - -- 5eb6882: Split `@backstage/plugin-scaffolder-backend-module-bitbucket` into - `@backstage/plugin-scaffolder-backend-module-bitbucket-cloud` and - `@backstage/plugin-scaffolder-backend-module-bitbucket-server`. - - `@backstage/plugin-scaffolder-backend-module-bitbucket` was **deprecated** in favor of these two replacements. - - Please use any of the two replacements depending on your needs. - - ```diff - - backend.add(import('@backstage/plugin-scaffolder-backend-module-bitbucket')); - + backend.add(import('@backstage/plugin-scaffolder-backend-module-bitbucket-cloud')); - + backend.add(import('@backstage/plugin-scaffolder-backend-module-bitbucket-server')); - ``` - -### Patch Changes - -- e9a5228: Exporting a default module for the new Backend System -- 8472188: Added or fixed the `repository` field in `package.json`. -- 6bb6f3e: Updated dependency `fs-extra` to `^11.2.0`. - Updated dependency `@types/fs-extra` to `^11.0.0`. -- fc98bb6: Enhanced the pull request action to allow for adding new content to the PR as described in this issue #21762 -- Updated dependencies - - @backstage/backend-common@0.21.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.0 - - @backstage/backend-plugin-api@0.6.10 - - @backstage/integration@1.9.0 - - @backstage/plugin-scaffolder-node@0.3.0 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.0 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - -## 0.2.0-next.3 - -### Patch Changes - -- 8472188: Added or fixed the `repository` field in `package.json`. -- Updated dependencies - - @backstage/backend-common@0.21.0-next.3 - - @backstage/integration@1.9.0-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.0-next.1 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.0-next.1 - - @backstage/plugin-scaffolder-node@0.3.0-next.3 - - @backstage/backend-plugin-api@0.6.10-next.3 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - -## 0.2.0-next.2 - -### Minor Changes - -- 5eb6882: Split `@backstage/plugin-scaffolder-backend-module-bitbucket` into - `@backstage/plugin-scaffolder-backend-module-bitbucket-cloud` and - `@backstage/plugin-scaffolder-backend-module-bitbucket-server`. - - `@backstage/plugin-scaffolder-backend-module-bitbucket` was **deprecated** in favor of these two replacements. - - Please use any of the two replacements depending on your needs. - - ```diff - - backend.add(import('@backstage/plugin-scaffolder-backend-module-bitbucket')); - + backend.add(import('@backstage/plugin-scaffolder-backend-module-bitbucket-cloud')); - + backend.add(import('@backstage/plugin-scaffolder-backend-module-bitbucket-server')); - ``` - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.0-next.0 - - @backstage/backend-common@0.21.0-next.2 - - @backstage/backend-plugin-api@0.6.10-next.2 - - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.0-next.0 - - @backstage/plugin-scaffolder-node@0.3.0-next.2 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/integration@1.9.0-next.0 - -## 0.1.2-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@0.6.10-next.1 - - @backstage/backend-common@0.21.0-next.1 - - @backstage/integration@1.9.0-next.0 - - @backstage/plugin-scaffolder-node@0.3.0-next.1 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - -## 0.1.2-next.0 - -### Patch Changes - -- e9a5228: Exporting a default module for the new Backend System -- fc98bb6: Enhanced the pull request action to allow for adding new content to the PR as described in this issue #21762 -- Updated dependencies - - @backstage/backend-common@0.21.0-next.0 - - @backstage/plugin-scaffolder-node@0.3.0-next.0 - - @backstage/backend-plugin-api@0.6.10-next.0 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/integration@1.8.0 - -## 0.1.1 - -### Patch Changes - -- a694f71: The Scaffolder builtin actions now contains an action for running pipelines from Bitbucket Cloud Rest API -- Updated dependencies - - @backstage/backend-common@0.20.1 - - @backstage/plugin-scaffolder-node@0.2.10 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/integration@1.8.0 - -## 0.1.1-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.20.1-next.2 - - @backstage/plugin-scaffolder-node@0.2.10-next.2 - -## 0.1.1-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.20.1-next.1 - - @backstage/integration@1.8.0 - - @backstage/config@1.1.1 - - @backstage/plugin-scaffolder-node@0.2.10-next.1 - - @backstage/errors@1.2.3 - -## 0.1.1-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.20.1-next.0 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/integration@1.8.0 - - @backstage/plugin-scaffolder-node@0.2.10-next.0 - -## 0.1.0 - -### Minor Changes - -- 219d7f0: Create new scaffolder module for external integrations - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.20.0 - - @backstage/plugin-scaffolder-node@0.2.9 - - @backstage/integration@1.8.0 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - -## 0.1.0-next.0 - -### Minor Changes - -- 219d7f0: Create new scaffolder module for external integrations - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-scaffolder-node@0.2.9-next.3 - - @backstage/backend-common@0.20.0-next.3 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/integration@1.8.0-next.1 diff --git a/plugins/scaffolder-backend-module-bitbucket/README.md b/plugins/scaffolder-backend-module-bitbucket/README.md deleted file mode 100644 index dde4207c0a..0000000000 --- a/plugins/scaffolder-backend-module-bitbucket/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# @backstage/plugin-scaffolder-backend-module-bitbucket - -**Deprecated!** - -Please use one of the following modules instead: - -- [@backstage/plugin-scaffolder-backend-module-bitbucket-cloud](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend-module-bitbucket-cloud) -- [@backstage/plugin-scaffolder-backend-module-bitbucket-server](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend-module-bitbucket-server). diff --git a/plugins/scaffolder-backend-module-bitbucket/catalog-info.yaml b/plugins/scaffolder-backend-module-bitbucket/catalog-info.yaml deleted file mode 100644 index b0a94d1c13..0000000000 --- a/plugins/scaffolder-backend-module-bitbucket/catalog-info.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: backstage-plugin-scaffolder-backend-module-bitbucket - title: '@backstage/plugin-scaffolder-backend-module-bitbucket' - description: The bitbucket module for @backstage/plugin-scaffolder-backend -spec: - lifecycle: experimental - type: backstage-backend-plugin-module - owner: maintainers diff --git a/plugins/scaffolder-backend-module-bitbucket/knip-report.md b/plugins/scaffolder-backend-module-bitbucket/knip-report.md deleted file mode 100644 index c8b2aa1788..0000000000 --- a/plugins/scaffolder-backend-module-bitbucket/knip-report.md +++ /dev/null @@ -1,8 +0,0 @@ -# Knip report - -## Unused dependencies (1) - -| Name | Location | Severity | -| :------- | :----------- | :------- | -| fs-extra | plugins/scaffolder-backend-module-bitbucket/package.json | error | - diff --git a/plugins/scaffolder-backend-module-bitbucket/package.json b/plugins/scaffolder-backend-module-bitbucket/package.json deleted file mode 100644 index d048f9dcc9..0000000000 --- a/plugins/scaffolder-backend-module-bitbucket/package.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "name": "@backstage/plugin-scaffolder-backend-module-bitbucket", - "version": "0.3.20-next.0", - "description": "The bitbucket module for @backstage/plugin-scaffolder-backend", - "backstage": { - "role": "backend-plugin-module", - "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" - }, - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "https://github.com/backstage/backstage", - "directory": "plugins/scaffolder-backend-module-bitbucket" - }, - "license": "Apache-2.0", - "exports": { - ".": "./src/index.ts", - "./package.json": "./package.json" - }, - "main": "src/index.ts", - "types": "src/index.ts", - "typesVersions": { - "*": { - "package.json": [ - "package.json" - ] - } - }, - "files": [ - "dist" - ], - "scripts": { - "build": "backstage-cli package build", - "clean": "backstage-cli package clean", - "lint": "backstage-cli package lint", - "prepack": "backstage-cli package prepack", - "postpack": "backstage-cli package postpack", - "start": "backstage-cli package start", - "test": "backstage-cli package test" - }, - "dependencies": { - "@backstage/backend-plugin-api": "workspace:^", - "@backstage/config": "workspace:^", - "@backstage/errors": "workspace:^", - "@backstage/integration": "workspace:^", - "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "workspace:^", - "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "workspace:^", - "@backstage/plugin-scaffolder-node": "workspace:^", - "fs-extra": "^11.2.0", - "yaml": "^2.0.0" - }, - "devDependencies": { - "@backstage/backend-test-utils": "workspace:^", - "@backstage/cli": "workspace:^", - "@backstage/plugin-scaffolder-node-test-utils": "workspace:^", - "msw": "^1.0.0" - }, - "deprecated": true -} diff --git a/plugins/scaffolder-backend-module-bitbucket/report.api.md b/plugins/scaffolder-backend-module-bitbucket/report.api.md deleted file mode 100644 index 33c9bcc9bd..0000000000 --- a/plugins/scaffolder-backend-module-bitbucket/report.api.md +++ /dev/null @@ -1,110 +0,0 @@ -## API Report File for "@backstage/plugin-scaffolder-backend-module-bitbucket" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -import { BackendFeature } from '@backstage/backend-plugin-api'; -import * as bitbucketCloud from '@backstage/plugin-scaffolder-backend-module-bitbucket-cloud'; -import * as bitbucketServer from '@backstage/plugin-scaffolder-backend-module-bitbucket-server'; -import { Config } from '@backstage/config'; -import { ScmIntegrationRegistry } from '@backstage/integration'; -import { TemplateAction } from '@backstage/plugin-scaffolder-node'; - -// @public @deprecated -const bitbucketModule: BackendFeature; -export default bitbucketModule; - -// @public @deprecated (undocumented) -export const createBitbucketPipelinesRunAction: (options: { - integrations: ScmIntegrationRegistry; -}) => TemplateAction< - { - workspace: string; - repo_slug: string; - body?: - | { - target?: - | { - type?: string | undefined; - source?: string | undefined; - selector?: - | { - type: string; - pattern: string; - } - | undefined; - pull_request?: - | { - id: string; - } - | undefined; - commit?: - | { - type: string; - hash: string; - } - | undefined; - destination?: string | undefined; - ref_name?: string | undefined; - ref_type?: string | undefined; - destination_commit?: - | { - hash: string; - } - | undefined; - } - | undefined; - variables?: - | { - key: string; - value: string; - secured: boolean; - }[] - | undefined; - } - | undefined; - token?: string | undefined; - }, - { - buildNumber?: number | undefined; - repoUrl?: string | undefined; - pipelinesUrl?: string | undefined; - }, - 'v2' ->; - -// @public @deprecated -export function createPublishBitbucketAction(options: { - integrations: ScmIntegrationRegistry; - config: Config; -}): TemplateAction< - { - repoUrl: string; - description?: string | undefined; - repoVisibility?: 'private' | 'public' | undefined; - defaultBranch?: string | undefined; - sourcePath?: string | undefined; - enableLFS?: boolean | undefined; - token?: string | undefined; - gitCommitMessage?: string | undefined; - gitAuthorName?: string | undefined; - gitAuthorEmail?: string | undefined; - signCommit?: boolean | undefined; - }, - { - remoteUrl?: string | undefined; - repoContentsUrl?: string | undefined; - commitHash?: string | undefined; - }, - 'v2' ->; - -// @public @deprecated (undocumented) -export const createPublishBitbucketCloudAction: typeof bitbucketCloud.createPublishBitbucketCloudAction; - -// @public @deprecated (undocumented) -export const createPublishBitbucketServerAction: typeof bitbucketServer.createPublishBitbucketServerAction; - -// @public @deprecated (undocumented) -export const createPublishBitbucketServerPullRequestAction: typeof bitbucketServer.createPublishBitbucketServerPullRequestAction; -``` diff --git a/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.examples.test.ts b/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.examples.test.ts deleted file mode 100644 index 8496405abf..0000000000 --- a/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.examples.test.ts +++ /dev/null @@ -1,368 +0,0 @@ -/* - * 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. - */ - -jest.mock('@backstage/plugin-scaffolder-node', () => { - return { - ...jest.requireActual('@backstage/plugin-scaffolder-node'), - initRepoAndPush: jest.fn().mockResolvedValue({ - commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', - }), - commitAndPushRepo: jest.fn().mockResolvedValue({ - commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', - }), - }; -}); - -import { createPublishBitbucketAction } from './bitbucket'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; -import { registerMswTestHooks } from '@backstage/backend-test-utils'; -import { ScmIntegrations } from '@backstage/integration'; -import { ConfigReader } from '@backstage/config'; -import { initRepoAndPush } from '@backstage/plugin-scaffolder-node'; -import yaml from 'yaml'; -import { sep } from 'node:path'; -import { examples } from './bitbucket.examples'; -import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; - -describe('publish:bitbucket', () => { - const config = new ConfigReader({ - integrations: { - bitbucket: [ - { - host: 'bitbucket.org', - token: 'tokenlols', - }, - { - host: 'hosted.bitbucket.com', - token: 'thing', - apiBaseUrl: 'https://hosted.bitbucket.com/rest/api/1.0', - }, - { - host: 'notoken.bitbucket.com', - }, - ], - }, - }); - - const integrations = ScmIntegrations.fromConfig(config); - const action = createPublishBitbucketAction({ integrations, config }); - const mockContext = createMockActionContext({ - input: { - repoUrl: 'bitbucket.org?workspace=workspace&project=project&repo=repo', - repoVisibility: 'private' as const, - }, - }); - const server = setupServer(); - registerMswTestHooks(server); - - beforeEach(() => { - jest.resetAllMocks(); - }); - - it('should call initAndPush with the correct values', async () => { - server.use( - rest.post( - 'https://api.bitbucket.org/2.0/repositories/workspace/repo', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - html: { - href: 'https://bitbucket.org/workspace/repo', - }, - clone: [ - { - name: 'https', - href: 'https://bitbucket.org/workspace/cloneurl', - }, - ], - }, - }), - ), - ), - ); - - await action.handler({ - ...mockContext, - input: yaml.parse(examples[0].example).steps[0].input, - }); - - expect(initRepoAndPush).toHaveBeenCalledWith({ - dir: mockContext.workspacePath, - remoteUrl: 'https://bitbucket.org/workspace/cloneurl', - defaultBranch: 'master', - auth: { username: 'x-token-auth', password: 'tokenlols' }, - logger: mockContext.logger, - commitMessage: 'initial commit', - gitAuthorInfo: { email: undefined, name: undefined }, - }); - }); - - it('should call initAndPush with the correct default branch', async () => { - server.use( - rest.post( - 'https://api.bitbucket.org/2.0/repositories/workspace/repo', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - html: { - href: 'https://bitbucket.org/workspace/repo', - }, - clone: [ - { - name: 'https', - href: 'https://bitbucket.org/workspace/cloneurl', - }, - ], - }, - }), - ), - ), - ); - - await action.handler({ - ...mockContext, - input: yaml.parse(examples[3].example).steps[0].input, - }); - - expect(initRepoAndPush).toHaveBeenCalledWith({ - dir: mockContext.workspacePath, - remoteUrl: 'https://bitbucket.org/workspace/cloneurl', - defaultBranch: 'main', - auth: { username: 'x-token-auth', password: 'tokenlols' }, - logger: mockContext.logger, - commitMessage: 'initial commit', - gitAuthorInfo: { email: undefined, name: undefined }, - }); - }); - - it('should call initAndPush with the specified source path', async () => { - server.use( - rest.post( - 'https://api.bitbucket.org/2.0/repositories/workspace/repo', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - html: { - href: 'https://bitbucket.org/workspace/repo', - }, - clone: [ - { - name: 'https', - href: 'https://bitbucket.org/workspace/cloneurl', - }, - ], - }, - }), - ), - ), - ); - - await action.handler({ - ...mockContext, - input: yaml.parse(examples[4].example).steps[0].input, - }); - expect(initRepoAndPush).toHaveBeenCalledWith({ - dir: `${mockContext.workspacePath}${sep}repoRoot`, - remoteUrl: 'https://bitbucket.org/workspace/cloneurl', - auth: { username: 'x-token-auth', password: 'tokenlols' }, - logger: mockContext.logger, - defaultBranch: 'master', - commitMessage: 'initial commit', - gitAuthorInfo: { email: undefined, name: undefined }, - }); - }); - - it('should call initAndPush with the authentication token', async () => { - server.use( - rest.post( - 'https://api.bitbucket.org/2.0/repositories/workspace/repo', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - html: { - href: 'https://bitbucket.org/workspace/repo', - }, - clone: [ - { - name: 'https', - href: 'https://bitbucket.org/workspace/cloneurl', - }, - ], - }, - }), - ), - ), - ); - - await action.handler({ - ...mockContext, - input: yaml.parse(examples[6].example).steps[0].input, - }); - expect(initRepoAndPush).toHaveBeenCalledWith({ - dir: mockContext.workspacePath, - remoteUrl: 'https://bitbucket.org/workspace/cloneurl', - auth: { username: 'x-token-auth', password: 'your-auth-token' }, - logger: mockContext.logger, - defaultBranch: 'master', - commitMessage: 'initial commit', - gitAuthorInfo: { - email: undefined, - name: undefined, - }, - }); - }); - - it('should call initAndPush with the custom commit message', async () => { - server.use( - rest.post( - 'https://api.bitbucket.org/2.0/repositories/workspace/repo', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - html: { - href: 'https://bitbucket.org/workspace/repo', - }, - clone: [ - { - name: 'https', - href: 'https://bitbucket.org/workspace/cloneurl', - }, - ], - }, - }), - ), - ), - ); - - await action.handler({ - ...mockContext, - input: yaml.parse(examples[7].example).steps[0].input, - }); - - expect(initRepoAndPush).toHaveBeenCalledWith({ - dir: mockContext.workspacePath, - remoteUrl: 'https://bitbucket.org/workspace/cloneurl', - auth: { username: 'x-token-auth', password: 'tokenlols' }, - logger: mockContext.logger, - defaultBranch: 'master', - commitMessage: 'Initial commit with custom message', - gitAuthorInfo: { email: undefined, name: undefined }, - }); - }); - - it('should call initAndPush with the custom author name and email for the commit.', async () => { - server.use( - rest.post( - 'https://api.bitbucket.org/2.0/repositories/workspace/repo', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - html: { - href: 'https://bitbucket.org/workspace/repo', - }, - clone: [ - { - name: 'https', - href: 'https://bitbucket.org/workspace/cloneurl', - }, - ], - }, - }), - ), - ), - ); - - await action.handler({ - ...mockContext, - input: yaml.parse(examples[8].example).steps[0].input, - }); - - expect(initRepoAndPush).toHaveBeenCalledWith({ - dir: mockContext.workspacePath, - remoteUrl: 'https://bitbucket.org/workspace/cloneurl', - auth: { username: 'x-token-auth', password: 'tokenlols' }, - logger: mockContext.logger, - defaultBranch: 'master', - commitMessage: 'initial commit', - gitAuthorInfo: { email: 'your.email@example.com', name: 'Your Name' }, - }); - }); - - describe('LFS for hosted bitbucket', () => { - const repoCreationResponse = { - links: { - self: [ - { - href: 'https://bitbucket.mycompany.com/projects/project/repos/repo', - }, - ], - clone: [ - { - name: 'http', - href: 'https://bitbucket.mycompany.com/scm/project/repo', - }, - ], - }, - }; - - it('should call the correct APIs to enable LFS if requested and the host is hosted bitbucket', async () => { - expect.assertions(1); - server.use( - rest.post( - 'https://hosted.bitbucket.com/rest/api/1.0/projects/project/repos', - (_, res, ctx) => { - return res( - ctx.status(201), - ctx.set('Content-Type', 'application/json'), - ctx.json(repoCreationResponse), - ); - }, - ), - rest.put( - 'https://hosted.bitbucket.com/rest/git-lfs/admin/projects/project/repos/repo/enabled', - (req, res, ctx) => { - expect(req.headers.get('Authorization')).toBe('Bearer thing'); - return res(ctx.status(204)); - }, - ), - ); - - await action.handler({ - ...mockContext, - input: yaml.parse(examples[5].example).steps[0].input, - }); - }); - }); -}); diff --git a/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.examples.ts b/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.examples.ts deleted file mode 100644 index 6248c991f8..0000000000 --- a/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.examples.ts +++ /dev/null @@ -1,203 +0,0 @@ -/* - * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; -import yaml from 'yaml'; - -export const examples: TemplateExample[] = [ - { - description: - 'Initializes a git repository with the content in the workspace, and publishes it to Bitbucket with the default configuration.', - example: yaml.stringify({ - steps: [ - { - id: 'publish', - action: 'publish:bitbucket', - name: 'Publish to Bitbucket', - input: { - repoUrl: - 'bitbucket.org?repo=repo&workspace=workspace&project=project', - }, - }, - ], - }), - }, - { - description: 'Initializes a Bitbucket repository with a description.', - example: yaml.stringify({ - steps: [ - { - id: 'publish', - action: 'publish:bitbucket', - name: 'Publish to Bitbucket', - input: { - repoUrl: - 'bitbucket.org?repo=repo&workspace=workspace&project=project', - description: 'Initialize a git repository', - }, - }, - ], - }), - }, - { - description: - 'Initializes a Bitbucket repository with public repo visibility, if not set defaults to private', - example: yaml.stringify({ - steps: [ - { - id: 'publish', - action: 'publish:bitbucket', - name: 'Publish to Bitbucket', - input: { - repoUrl: - 'bitbucket.org?repo=repo&workspace=workspace&project=project', - repoVisibility: 'public', - }, - }, - ], - }), - }, - { - description: - 'Initializes a Bitbucket repository with a default branch, if not set defaults to master', - example: yaml.stringify({ - steps: [ - { - id: 'publish', - action: 'publish:bitbucket', - name: 'Publish to Bitbucket', - input: { - repoUrl: - 'bitbucket.org?repo=repo&workspace=workspace&project=project', - defaultBranch: 'main', - }, - }, - ], - }), - }, - { - description: - 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository', - example: yaml.stringify({ - steps: [ - { - id: 'publish', - action: 'publish:bitbucket', - name: 'Publish to Bitbucket', - input: { - repoUrl: - 'bitbucket.org?repo=repo&workspace=workspace&project=project', - sourcePath: './repoRoot', - }, - }, - ], - }), - }, - { - description: 'Initializes a Bitbucket repository with LFS enabled', - example: yaml.stringify({ - steps: [ - { - id: 'publish', - action: 'publish:bitbucket', - name: 'Publish to Bitbucket', - input: { - repoUrl: 'hosted.bitbucket.com?project=project&repo=repo', - enableLFS: true, - }, - }, - ], - }), - }, - { - description: - 'Initializes a Bitbucket repository with a custom authentication token', - example: yaml.stringify({ - steps: [ - { - id: 'publish', - action: 'publish:bitbucket', - name: 'Publish to Bitbucket', - input: { - repoUrl: - 'bitbucket.org?repo=repo&workspace=workspace&project=project', - token: 'your-auth-token', - }, - }, - ], - }), - }, - { - description: - 'Initializes a Bitbucket repository with an initial commit message, if not set defaults to `initial commit`', - example: yaml.stringify({ - steps: [ - { - id: 'publish', - action: 'publish:bitbucket', - name: 'Publish to Bitbucket', - input: { - repoUrl: - 'bitbucket.org?repo=repo&workspace=workspace&project=project', - gitCommitMessage: 'Initial commit with custom message', - }, - }, - ], - }), - }, - { - description: 'Initializes a Bitbucket repository with a custom author', - example: yaml.stringify({ - steps: [ - { - id: 'publish', - action: 'publish:bitbucket', - name: 'Publish to Bitbucket', - input: { - repoUrl: - 'bitbucket.org?repo=repo&workspace=workspace&project=project', - gitAuthorName: 'Your Name', - gitAuthorEmail: 'your.email@example.com', - }, - }, - ], - }), - }, - { - description: - 'Initializes a Bitbucket repository with all properties being set', - example: yaml.stringify({ - steps: [ - { - id: 'publish', - action: 'publish:bitbucket', - name: 'Publish to Bitbucket', - input: { - repoUrl: - 'bitbucket.org?repo=repo&workspace=workspace&project=project', - description: 'Initialize a git repository', - repoVisibility: 'public', - defaultBranch: 'main', - token: 'your-auth-token', - gitCommitMessage: 'Initial commit with custom message', - gitAuthorName: 'Your Name', - gitAuthorEmail: 'your.email@example.com', - }, - }, - ], - }), - }, -]; diff --git a/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.test.ts b/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.test.ts deleted file mode 100644 index 654b0fffcb..0000000000 --- a/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.test.ts +++ /dev/null @@ -1,619 +0,0 @@ -/* - * Copyright 2021 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. - */ - -jest.mock('@backstage/plugin-scaffolder-node', () => { - return { - ...jest.requireActual('@backstage/plugin-scaffolder-node'), - initRepoAndPush: jest.fn().mockResolvedValue({ - commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', - }), - commitAndPushRepo: jest.fn().mockResolvedValue({ - commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', - }), - }; -}); -import { createPublishBitbucketAction } from './bitbucket'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; -import { registerMswTestHooks } from '@backstage/backend-test-utils'; -import { ScmIntegrations } from '@backstage/integration'; -import { ConfigReader } from '@backstage/config'; -import { initRepoAndPush } from '@backstage/plugin-scaffolder-node'; -import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; - -describe('publish:bitbucket', () => { - const config = new ConfigReader({ - integrations: { - bitbucket: [ - { - host: 'bitbucket.org', - token: 'tokenlols', - }, - { - host: 'hosted.bitbucket.com', - token: 'thing', - apiBaseUrl: 'https://hosted.bitbucket.com/rest/api/1.0', - }, - { - host: 'notoken.bitbucket.com', - }, - ], - }, - }); - - const integrations = ScmIntegrations.fromConfig(config); - const action = createPublishBitbucketAction({ integrations, config }); - const mockContext = createMockActionContext({ - input: { - repoUrl: 'bitbucket.org?workspace=workspace&project=project&repo=repo', - repoVisibility: 'private' as const, - }, - }); - const server = setupServer(); - registerMswTestHooks(server); - - beforeEach(() => { - jest.resetAllMocks(); - }); - - it('should throw an error when the repoUrl is not well formed', async () => { - await expect( - action.handler({ - ...mockContext, - input: { repoUrl: 'bitbucket.org?project=project&repo=repo' }, - }), - ).rejects.toThrow(/missing workspace/); - - await expect( - action.handler({ - ...mockContext, - input: { repoUrl: 'bitbucket.org?workspace=workspace&repo=repo' }, - }), - ).rejects.toThrow(/missing project/); - - await expect( - action.handler({ - ...mockContext, - input: { repoUrl: 'bitbucket.org?workspace=workspace&project=project' }, - }), - ).rejects.toThrow(/missing repo/); - }); - - it('should throw if there is no integration config provided', async () => { - await expect( - action.handler({ - ...mockContext, - input: { - repoUrl: 'missing.com?workspace=workspace&project=project&repo=repo', - }, - }), - ).rejects.toThrow(/No matching integration configuration/); - }); - - it('should throw if there is no token in the integration config that is returned', async () => { - await expect( - action.handler({ - ...mockContext, - input: { - repoUrl: - 'notoken.bitbucket.com?workspace=workspace&project=project&repo=repo', - }, - }), - ).rejects.toThrow(/Authorization has not been provided for Bitbucket/); - }); - - it('should call the correct APIs when the host is bitbucket cloud', async () => { - expect.assertions(2); - server.use( - rest.post( - 'https://api.bitbucket.org/2.0/repositories/workspace/repo', - (req, res, ctx) => { - expect(req.headers.get('Authorization')).toBe('Bearer tokenlols'); - expect(req.body).toEqual({ - is_private: true, - scm: 'git', - project: { key: 'project' }, - }); - return res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - html: { - href: 'https://bitbucket.org/workspace/repo', - }, - clone: [ - { - name: 'https', - href: 'https://bitbucket.org/workspace/repo', - }, - ], - }, - }), - ); - }, - ), - ); - - await action.handler({ - ...mockContext, - input: { - ...mockContext.input, - repoUrl: 'bitbucket.org?workspace=workspace&project=project&repo=repo', - }, - }); - }); - - it('should call the correct APIs when the host is hosted bitbucket', async () => { - expect.assertions(2); - server.use( - rest.post( - 'https://hosted.bitbucket.com/rest/api/1.0/projects/project/repos', - (req, res, ctx) => { - expect(req.headers.get('Authorization')).toBe('Bearer thing'); - expect(req.body).toEqual({ public: false, name: 'repo' }); - return res( - ctx.status(201), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - self: [ - { - href: 'https://bitbucket.mycompany.com/projects/project/repos/repo', - }, - ], - clone: [ - { - name: 'http', - href: 'https://bitbucket.mycompany.com/scm/project/repo', - }, - ], - }, - }), - ); - }, - ), - ); - - await action.handler({ - ...mockContext, - input: { - ...mockContext.input, - repoUrl: 'hosted.bitbucket.com?project=project&repo=repo', - }, - }); - }); - - it('should work if the token is provided through ctx.input', async () => { - expect.assertions(2); - server.use( - rest.post( - 'https://notoken.bitbucket.com/rest/api/1.0/projects/project/repos', - (req, res, ctx) => { - expect(req.headers.get('Authorization')).toBe('Bearer lols'); - expect(req.body).toEqual({ public: false, name: 'repo' }); - return res( - ctx.status(201), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - self: [ - { - href: 'https://bitbucket.mycompany.com/projects/project/repos/repo', - }, - ], - clone: [ - { - name: 'http', - href: 'https://bitbucket.mycompany.com/scm/project/repo', - }, - ], - }, - }), - ); - }, - ), - ); - await action.handler({ - ...mockContext, - input: { - repoUrl: 'notoken.bitbucket.com?project=project&repo=repo', - token: 'lols', - }, - }); - }); - - describe('LFS for hosted bitbucket', () => { - const repoCreationResponse = { - links: { - self: [ - { - href: 'https://bitbucket.mycompany.com/projects/project/repos/repo', - }, - ], - clone: [ - { - name: 'http', - href: 'https://bitbucket.mycompany.com/scm/project/repo', - }, - ], - }, - }; - - it('should call the correct APIs to enable LFS if requested and the host is hosted bitbucket', async () => { - expect.assertions(1); - server.use( - rest.post( - 'https://hosted.bitbucket.com/rest/api/1.0/projects/project/repos', - (_, res, ctx) => { - return res( - ctx.status(201), - ctx.set('Content-Type', 'application/json'), - ctx.json(repoCreationResponse), - ); - }, - ), - rest.put( - 'https://hosted.bitbucket.com/rest/git-lfs/admin/projects/project/repos/repo/enabled', - (req, res, ctx) => { - expect(req.headers.get('Authorization')).toBe('Bearer thing'); - return res(ctx.status(204)); - }, - ), - ); - - await action.handler({ - ...mockContext, - input: { - ...mockContext.input, - repoUrl: 'hosted.bitbucket.com?project=project&repo=repo', - enableLFS: true, - }, - }); - }); - - it('should report an error if enabling LFS fails', async () => { - server.use( - rest.post( - 'https://hosted.bitbucket.com/rest/api/1.0/projects/project/repos', - (_, res, ctx) => { - return res( - ctx.status(201), - ctx.set('Content-Type', 'application/json'), - ctx.json(repoCreationResponse), - ); - }, - ), - rest.put( - 'https://hosted.bitbucket.com/rest/git-lfs/admin/projects/project/repos/repo/enabled', - (_, res, ctx) => { - return res(ctx.status(500)); - }, - ), - ); - - await expect( - action.handler({ - ...mockContext, - input: { - ...mockContext.input, - repoUrl: 'hosted.bitbucket.com?project=project&repo=repo', - enableLFS: true, - }, - }), - ).rejects.toThrow(/Failed to enable LFS/); - }); - }); - - it('should call initAndPush with the correct values', async () => { - server.use( - rest.post( - 'https://api.bitbucket.org/2.0/repositories/workspace/repo', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - html: { - href: 'https://bitbucket.org/workspace/repo', - }, - clone: [ - { - name: 'https', - href: 'https://bitbucket.org/workspace/cloneurl', - }, - ], - }, - }), - ), - ), - ); - - await action.handler(mockContext); - - expect(initRepoAndPush).toHaveBeenCalledWith({ - dir: mockContext.workspacePath, - remoteUrl: 'https://bitbucket.org/workspace/cloneurl', - defaultBranch: 'master', - auth: { username: 'x-token-auth', password: 'tokenlols' }, - logger: mockContext.logger, - commitMessage: 'initial commit', - gitAuthorInfo: {}, - }); - }); - - it('should call initAndPush with the correct default branch', async () => { - server.use( - rest.post( - 'https://api.bitbucket.org/2.0/repositories/workspace/repo', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - html: { - href: 'https://bitbucket.org/workspace/repo', - }, - clone: [ - { - name: 'https', - href: 'https://bitbucket.org/workspace/cloneurl', - }, - ], - }, - }), - ), - ), - ); - - await action.handler({ - ...mockContext, - input: { - ...mockContext.input, - defaultBranch: 'main', - }, - }); - - expect(initRepoAndPush).toHaveBeenCalledWith({ - dir: mockContext.workspacePath, - remoteUrl: 'https://bitbucket.org/workspace/cloneurl', - defaultBranch: 'main', - auth: { username: 'x-token-auth', password: 'tokenlols' }, - logger: mockContext.logger, - commitMessage: 'initial commit', - gitAuthorInfo: {}, - }); - }); - - it('should call initAndPush with the configured defaultAuthor', async () => { - const customAuthorConfig = new ConfigReader({ - integrations: { - bitbucket: [ - { - host: 'bitbucket.org', - token: 'tokenlols', - }, - { - host: 'hosted.bitbucket.com', - token: 'thing', - apiBaseUrl: 'https://hosted.bitbucket.com/rest/api/1.0', - }, - { - host: 'notoken.bitbucket.com', - }, - ], - }, - scaffolder: { - defaultAuthor: { - name: 'Test', - email: 'example@example.com', - }, - }, - }); - - const customAuthorIntegrations = - ScmIntegrations.fromConfig(customAuthorConfig); - const customAuthorAction = createPublishBitbucketAction({ - integrations: customAuthorIntegrations, - config: customAuthorConfig, - }); - - server.use( - rest.post( - 'https://api.bitbucket.org/2.0/repositories/workspace/repo', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - html: { - href: 'https://bitbucket.org/workspace/repo', - }, - clone: [ - { - name: 'https', - href: 'https://bitbucket.org/workspace/cloneurl', - }, - ], - }, - }), - ), - ), - ); - - await customAuthorAction.handler(mockContext); - - expect(initRepoAndPush).toHaveBeenCalledWith({ - dir: mockContext.workspacePath, - remoteUrl: 'https://bitbucket.org/workspace/cloneurl', - auth: { username: 'x-token-auth', password: 'tokenlols' }, - logger: mockContext.logger, - defaultBranch: 'master', - commitMessage: 'initial commit', - gitAuthorInfo: { name: 'Test', email: 'example@example.com' }, - }); - }); - - it('should call initAndPush with the configured defaultCommitMessage', async () => { - const customAuthorConfig = new ConfigReader({ - integrations: { - bitbucket: [ - { - host: 'bitbucket.org', - token: 'tokenlols', - }, - { - host: 'hosted.bitbucket.com', - token: 'thing', - apiBaseUrl: 'https://hosted.bitbucket.com/rest/api/1.0', - }, - { - host: 'notoken.bitbucket.com', - }, - ], - }, - scaffolder: { - defaultCommitMessage: 'Test commit message', - }, - }); - - const customAuthorIntegrations = - ScmIntegrations.fromConfig(customAuthorConfig); - const customAuthorAction = createPublishBitbucketAction({ - integrations: customAuthorIntegrations, - config: customAuthorConfig, - }); - - server.use( - rest.post( - 'https://api.bitbucket.org/2.0/repositories/workspace/repo', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - html: { - href: 'https://bitbucket.org/workspace/repo', - }, - clone: [ - { - name: 'https', - href: 'https://bitbucket.org/workspace/cloneurl', - }, - ], - }, - }), - ), - ), - ); - - await customAuthorAction.handler(mockContext); - - expect(initRepoAndPush).toHaveBeenCalledWith({ - dir: mockContext.workspacePath, - remoteUrl: 'https://bitbucket.org/workspace/cloneurl', - auth: { username: 'x-token-auth', password: 'tokenlols' }, - logger: mockContext.logger, - defaultBranch: 'master', - commitMessage: 'initial commit', - gitAuthorInfo: { email: undefined, name: undefined }, - }); - }); - - it('should call outputs with the correct urls', async () => { - server.use( - rest.post( - 'https://api.bitbucket.org/2.0/repositories/workspace/repo', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - html: { - href: 'https://bitbucket.org/workspace/repo', - }, - clone: [ - { - name: 'https', - href: 'https://bitbucket.org/workspace/cloneurl', - }, - ], - }, - }), - ), - ), - ); - - await action.handler(mockContext); - - expect(mockContext.output).toHaveBeenCalledWith( - 'remoteUrl', - 'https://bitbucket.org/workspace/cloneurl', - ); - expect(mockContext.output).toHaveBeenCalledWith( - 'repoContentsUrl', - 'https://bitbucket.org/workspace/repo/src/master', - ); - }); - - it('should call outputs with the correct urls with correct default branch', async () => { - server.use( - rest.post( - 'https://api.bitbucket.org/2.0/repositories/workspace/repo', - (_, res, ctx) => - res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json({ - links: { - html: { - href: 'https://bitbucket.org/workspace/repo', - }, - clone: [ - { - name: 'https', - href: 'https://bitbucket.org/workspace/cloneurl', - }, - ], - }, - }), - ), - ), - ); - - await action.handler({ - ...mockContext, - input: { - ...mockContext.input, - defaultBranch: 'main', - }, - }); - - expect(mockContext.output).toHaveBeenCalledWith( - 'remoteUrl', - 'https://bitbucket.org/workspace/cloneurl', - ); - expect(mockContext.output).toHaveBeenCalledWith( - 'repoContentsUrl', - 'https://bitbucket.org/workspace/repo/src/main', - ); - }); -}); diff --git a/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.ts b/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.ts deleted file mode 100644 index 113e642ee8..0000000000 --- a/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.ts +++ /dev/null @@ -1,450 +0,0 @@ -/* - * Copyright 2021 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 { InputError } from '@backstage/errors'; -import { - BitbucketIntegrationConfig, - ScmIntegrationRegistry, -} from '@backstage/integration'; -import { - createTemplateAction, - getRepoSourceDirectory, - initRepoAndPush, - parseRepoUrl, -} from '@backstage/plugin-scaffolder-node'; -import { Config } from '@backstage/config'; -import { examples } from './bitbucket.examples'; - -const createBitbucketCloudRepository = async (opts: { - workspace: string; - project: string; - repo: string; - description?: string; - repoVisibility: 'private' | 'public'; - mainBranch: string; - authorization: string; - apiBaseUrl: string; -}) => { - const { - workspace, - project, - repo, - description, - repoVisibility, - mainBranch, - authorization, - apiBaseUrl, - } = opts; - - const options: RequestInit = { - method: 'POST', - body: JSON.stringify({ - scm: 'git', - description: description, - is_private: repoVisibility === 'private', - project: { key: project }, - }), - headers: { - Authorization: authorization, - 'Content-Type': 'application/json', - }, - }; - - let response: Response; - try { - response = await fetch( - `${apiBaseUrl}/repositories/${workspace}/${repo}`, - options, - ); - } catch (e) { - throw new Error(`Unable to create repository, ${e}`); - } - - if (response.status !== 200) { - throw new Error( - `Unable to create repository, ${response.status} ${ - response.statusText - }, ${await response.text()}`, - ); - } - - const r = await response.json(); - let remoteUrl = ''; - for (const link of r.links.clone) { - if (link.name === 'https') { - remoteUrl = link.href; - } - } - - // "mainbranch.name" cannot be set neither at create nor update of the repo - // the first pushed branch will be set as "main branch" then - const repoContentsUrl = `${r.links.html.href}/src/${mainBranch}`; - return { remoteUrl, repoContentsUrl }; -}; - -const createBitbucketServerRepository = async (opts: { - project: string; - repo: string; - description?: string; - repoVisibility: 'private' | 'public'; - authorization: string; - apiBaseUrl: string; -}) => { - const { - project, - repo, - description, - authorization, - repoVisibility, - apiBaseUrl, - } = opts; - - let response: Response; - const options: RequestInit = { - method: 'POST', - body: JSON.stringify({ - name: repo, - description: description, - public: repoVisibility === 'public', - }), - headers: { - Authorization: authorization, - 'Content-Type': 'application/json', - }, - }; - - try { - response = await fetch(`${apiBaseUrl}/projects/${project}/repos`, options); - } catch (e) { - throw new Error(`Unable to create repository, ${e}`); - } - - if (response.status !== 201) { - throw new Error( - `Unable to create repository, ${response.status} ${ - response.statusText - }, ${await response.text()}`, - ); - } - - const r = await response.json(); - let remoteUrl = ''; - for (const link of r.links.clone) { - if (link.name === 'http') { - remoteUrl = link.href; - } - } - - const repoContentsUrl = `${r.links.self[0].href}`; - return { remoteUrl, repoContentsUrl }; -}; - -const getAuthorizationHeader = (config: BitbucketIntegrationConfig) => { - if (config.username && (config.token ?? config.appPassword)) { - const buffer = Buffer.from( - `${config.username}:${config.token ?? config.appPassword}`, - 'utf8', - ); - - return `Basic ${buffer.toString('base64')}`; - } - - if (config.token) { - return `Bearer ${config.token}`; - } - - throw new Error( - `Authorization has not been provided for Bitbucket. Please add either provide a username and token or username and appPassword to the Integrations config`, - ); -}; - -const performEnableLFS = async (opts: { - authorization: string; - host: string; - project: string; - repo: string; -}) => { - const { authorization, host, project, repo } = opts; - - const options: RequestInit = { - method: 'PUT', - headers: { - Authorization: authorization, - }, - }; - - const { ok, status, statusText } = await fetch( - `https://${host}/rest/git-lfs/admin/projects/${project}/repos/${repo}/enabled`, - options, - ); - - if (!ok) - throw new Error( - `Failed to enable LFS in the repository, ${status}: ${statusText}`, - ); -}; - -/** - * Creates a new action that initializes a git repository of the content in the workspace - * and publishes it to Bitbucket. - * @public - * @deprecated in favor of "createPublishBitbucketCloudAction" by \@backstage/plugin-scaffolder-backend-module-bitbucket-cloud and "createPublishBitbucketServerAction" by \@backstage/plugin-scaffolder-backend-module-bitbucket-server - */ -export function createPublishBitbucketAction(options: { - integrations: ScmIntegrationRegistry; - config: Config; -}) { - const { integrations, config } = options; - - return createTemplateAction({ - id: 'publish:bitbucket', - description: - 'Initializes a git repository of the content in the workspace, and publishes it to Bitbucket.', - examples, - schema: { - input: { - repoUrl: z => - z.string({ - description: 'Repository Location', - }), - description: z => - z - .string({ - description: 'Repository Description', - }) - .optional(), - repoVisibility: z => - z - .enum(['private', 'public'], { - description: 'Repository Visibility', - }) - .optional(), - defaultBranch: z => - z - .string({ - description: `Sets the default branch on the repository. The default value is 'master'`, - }) - .optional(), - sourcePath: z => - z - .string({ - description: - 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.', - }) - .optional(), - enableLFS: z => - z - .boolean({ - description: - 'Enable LFS for the repository. Only available for hosted Bitbucket.', - }) - .optional(), - token: z => - z - .string({ - description: 'The token to use for authorization to BitBucket', - }) - .optional(), - gitCommitMessage: z => - z - .string({ - description: `Sets the commit message on the repository. The default value is 'initial commit'`, - }) - .optional(), - gitAuthorName: z => - z - .string({ - description: `Sets the default author name for the commit. The default value is 'Scaffolder'`, - }) - .optional(), - gitAuthorEmail: z => - z - .string({ - description: `Sets the default author email for the commit.`, - }) - .optional(), - signCommit: z => - z - .boolean({ - description: 'Sign commit with configured PGP private key', - }) - .optional(), - }, - output: { - remoteUrl: z => - z - .string({ - description: 'A URL to the repository with the provider', - }) - .optional(), - repoContentsUrl: z => - z - .string({ - description: 'A URL to the root of the repository', - }) - .optional(), - commitHash: z => - z - .string({ - description: 'The git commit hash of the initial commit', - }) - .optional(), - }, - }, - async handler(ctx) { - ctx.logger.warn( - `[Deprecated] Please migrate the use of action "publish:bitbucket" to "publish:bitbucketCloud" or "publish:bitbucketServer".`, - ); - const { - repoUrl, - description, - defaultBranch = 'master', - repoVisibility = 'private', - enableLFS = false, - gitCommitMessage = 'initial commit', - gitAuthorName, - gitAuthorEmail, - signCommit, - } = ctx.input; - - const { workspace, project, repo, host } = parseRepoUrl( - repoUrl, - integrations, - ); - - // Workspace is only required for bitbucket cloud - if (host === 'bitbucket.org') { - if (!workspace) { - throw new InputError( - `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`, - ); - } - } - - // Project is required for both bitbucket cloud and bitbucket server - if (!project) { - throw new InputError( - `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`, - ); - } - - const integrationConfig = integrations.bitbucket.byHost(host); - - if (!integrationConfig) { - throw new InputError( - `No matching integration configuration for host ${host}, please check your integrations config`, - ); - } - - const authorization = getAuthorizationHeader( - ctx.input.token - ? { - host: integrationConfig.config.host, - apiBaseUrl: integrationConfig.config.apiBaseUrl, - token: ctx.input.token, - } - : integrationConfig.config, - ); - - const apiBaseUrl = integrationConfig.config.apiBaseUrl; - - const createMethod = - host === 'bitbucket.org' - ? createBitbucketCloudRepository - : createBitbucketServerRepository; - - const { remoteUrl, repoContentsUrl } = await ctx.checkpoint({ - key: `create.repo.${host}.${repo}`, - fn: async () => - createMethod({ - authorization, - workspace: workspace || '', - project, - repo, - repoVisibility, - mainBranch: defaultBranch, - description, - apiBaseUrl, - }), - }); - - const gitAuthorInfo = { - name: gitAuthorName - ? gitAuthorName - : config.getOptionalString('scaffolder.defaultAuthor.name'), - email: gitAuthorEmail - ? gitAuthorEmail - : config.getOptionalString('scaffolder.defaultAuthor.email'), - }; - const signingKey = - integrationConfig.config.commitSigningKey ?? - config.getOptionalString('scaffolder.defaultCommitSigningKey'); - if (signCommit && !signingKey) { - throw new Error( - 'Signing commits is enabled but no signing key is provided in the configuration', - ); - } - - let auth; - - if (ctx.input.token) { - auth = { - username: 'x-token-auth', - password: ctx.input.token, - }; - } else { - auth = { - username: integrationConfig.config.username - ? integrationConfig.config.username - : 'x-token-auth', - password: integrationConfig.config.appPassword - ? integrationConfig.config.appPassword - : integrationConfig.config.token ?? '', - }; - } - - const commitHash = await ctx.checkpoint({ - key: `init.repo.and.push${host}.${repo}`, - fn: async () => { - const commitResult = await initRepoAndPush({ - dir: getRepoSourceDirectory( - ctx.workspacePath, - ctx.input.sourcePath, - ), - remoteUrl, - auth, - defaultBranch, - logger: ctx.logger, - commitMessage: gitCommitMessage - ? gitCommitMessage - : config.getOptionalString('scaffolder.defaultCommitMessage'), - gitAuthorInfo, - signingKey: signCommit ? signingKey : undefined, - }); - return commitResult?.commitHash; - }, - }); - - if (enableLFS && host !== 'bitbucket.org') { - await performEnableLFS({ authorization, host, project, repo }); - } - - ctx.output('commitHash', commitHash); - ctx.output('remoteUrl', remoteUrl); - ctx.output('repoContentsUrl', repoContentsUrl); - }, - }); -} diff --git a/plugins/scaffolder-backend-module-bitbucket/src/deprecated.ts b/plugins/scaffolder-backend-module-bitbucket/src/deprecated.ts deleted file mode 100644 index a9daa771f0..0000000000 --- a/plugins/scaffolder-backend-module-bitbucket/src/deprecated.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2024 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 * as bitbucketCloud from '@backstage/plugin-scaffolder-backend-module-bitbucket-cloud'; -import * as bitbucketServer from '@backstage/plugin-scaffolder-backend-module-bitbucket-server'; - -export { createPublishBitbucketAction } from './actions/bitbucket'; - -/** - * @public - * @deprecated use import from `@backstage/plugin-scaffolder-backend-module-bitbucket-cloud` instead - */ -export const createPublishBitbucketCloudAction = - bitbucketCloud.createPublishBitbucketCloudAction; - -/** - * @public - * @deprecated use import from `@backstage/plugin-scaffolder-backend-module-bitbucket-cloud` instead - */ -export const createBitbucketPipelinesRunAction = - bitbucketCloud.createBitbucketPipelinesRunAction; - -/** - * @public - * @deprecated use import from `@backstage/plugin-scaffolder-backend-module-bitbucket-server` instead - */ -export const createPublishBitbucketServerAction = - bitbucketServer.createPublishBitbucketServerAction; - -/** - * @public - * @deprecated use import from `@backstage/plugin-scaffolder-backend-module-bitbucket-server` instead - */ -export const createPublishBitbucketServerPullRequestAction = - bitbucketServer.createPublishBitbucketServerPullRequestAction; diff --git a/plugins/scaffolder-backend-module-bitbucket/src/index.ts b/plugins/scaffolder-backend-module-bitbucket/src/index.ts deleted file mode 100644 index 7f68a3f395..0000000000 --- a/plugins/scaffolder-backend-module-bitbucket/src/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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. - */ - -/** - * A module for the scaffolder backend that lets you interact with bitbucket - * - * @packageDocumentation - */ - -export * from './deprecated'; -export { bitbucketModule as default } from './module'; diff --git a/plugins/scaffolder-backend-module-bitbucket/src/module.ts b/plugins/scaffolder-backend-module-bitbucket/src/module.ts deleted file mode 100644 index 884c8be004..0000000000 --- a/plugins/scaffolder-backend-module-bitbucket/src/module.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2024 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 { - coreServices, - createBackendModule, -} from '@backstage/backend-plugin-api'; -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; -import { - createBitbucketPipelinesRunAction, - createPublishBitbucketCloudAction, - createPublishBitbucketServerAction, - createPublishBitbucketServerPullRequestAction, -} from './deprecated'; -import { ScmIntegrations } from '@backstage/integration'; - -/** - * The Bitbucket Module for the Scaffolder Backend - * @public - * @deprecated use module by \@backstage/plugin-scaffolder-backend-module-bitbucket-cloud or \@backstage/plugin-scaffolder-backend-module-bitbucket-server instead - */ -export const bitbucketModule = createBackendModule({ - moduleId: 'bitbucket', - pluginId: 'scaffolder', - register({ registerInit }) { - registerInit({ - deps: { - scaffolder: scaffolderActionsExtensionPoint, - config: coreServices.rootConfig, - }, - async init({ scaffolder, config }) { - const integrations = ScmIntegrations.fromConfig(config); - - scaffolder.addActions( - createPublishBitbucketCloudAction({ integrations, config }), - createPublishBitbucketServerAction({ integrations, config }), - createPublishBitbucketServerPullRequestAction({ - integrations, - config, - }), - createBitbucketPipelinesRunAction({ integrations }), - ); - }, - }); - }, -}); diff --git a/plugins/scaffolder-common/src/ScaffolderClient.ts b/plugins/scaffolder-common/src/ScaffolderClient.ts index d8aa5106c9..3aaa7ebefc 100644 --- a/plugins/scaffolder-common/src/ScaffolderClient.ts +++ b/plugins/scaffolder-common/src/ScaffolderClient.ts @@ -125,13 +125,6 @@ export class ScaffolderClient implements ScaffolderApi { ): Promise { const integrations = [ ...this.scmIntegrationsApi.azure.list(), - ...this.scmIntegrationsApi.bitbucket - .list() - .filter( - item => - !this.scmIntegrationsApi.bitbucketCloud.byHost(item.config.host) && - !this.scmIntegrationsApi.bitbucketServer.byHost(item.config.host), - ), ...this.scmIntegrationsApi.bitbucketCloud.list(), ...this.scmIntegrationsApi.bitbucketServer.list(), ...this.scmIntegrationsApi.gerrit.list(), diff --git a/plugins/scaffolder-node/src/actions/util.test.ts b/plugins/scaffolder-node/src/actions/util.test.ts index 792e306d00..8afbbf553a 100644 --- a/plugins/scaffolder-node/src/actions/util.test.ts +++ b/plugins/scaffolder-node/src/actions/util.test.ts @@ -54,11 +54,11 @@ describe('scaffolder action utils', () => { /No matching integration configuration for host/, ); }); - describe('bitbucket', () => { - beforeEach(() => byHost.mockReturnValue({ type: 'bitbucket' })); + describe('bitbucketCloud', () => { + beforeEach(() => byHost.mockReturnValue({ type: 'bitbucketCloud' })); describe('cloud', () => { const [host, workspace, project, repo] = [ - 'www.bitbucket.org', + 'bitbucket.org', 'foo', 'bar', 'baz', @@ -97,6 +97,9 @@ describe('scaffolder action utils', () => { repo, })); }); + }); + describe('bitbucketServer', () => { + beforeEach(() => byHost.mockReturnValue({ type: 'bitbucketServer' })); describe('other', () => { const [host, project, repo] = ['bitbucket.other', 'foo', 'bar']; it('requires project', () => diff --git a/plugins/scaffolder-node/src/actions/util.ts b/plugins/scaffolder-node/src/actions/util.ts index 0e90122cdd..e698306a45 100644 --- a/plugins/scaffolder-node/src/actions/util.ts +++ b/plugins/scaffolder-node/src/actions/util.ts @@ -84,10 +84,11 @@ export const parseRepoUrl = ( ]), ); switch (type) { - case 'bitbucket': { - if (host === 'www.bitbucket.org') { - checkRequiredParams(parsed, 'workspace'); - } + case 'bitbucketCloud': { + checkRequiredParams(parsed, 'workspace', 'project', 'repo'); + break; + } + case 'bitbucketServer': { checkRequiredParams(parsed, 'project', 'repo'); break; } diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/validation.test.ts b/plugins/scaffolder/src/components/fields/RepoUrlPicker/validation.test.ts index 2b59775d3a..16552c1292 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/validation.test.ts +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/validation.test.ts @@ -28,10 +28,12 @@ describe('RepoPicker Validation', () => { const config = new ConfigReader({ integrations: { - bitbucket: [ + bitbucketCloud: [ { host: 'bitbucket.org', }, + ], + bitbucketServer: [ { host: 'server.bitbucket.com', }, diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/validation.ts b/plugins/scaffolder/src/components/fields/RepoUrlPicker/validation.ts index 682fc6bb2a..2f3c5deeb9 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/validation.ts +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/validation.ts @@ -39,9 +39,16 @@ export const repoPickerValidation = ( 'Incomplete repository location provided, host not provided', ); } else { - if (integrationApi?.byHost(host)?.type === 'bitbucket') { + const integrationType = integrationApi?.byHost(host)?.type; + if ( + integrationType === 'bitbucketCloud' || + integrationType === 'bitbucketServer' + ) { // workspace is only applicable for bitbucket cloud - if (host === 'bitbucket.org' && !searchParams.get('workspace')) { + if ( + integrationType === 'bitbucketCloud' && + !searchParams.get('workspace') + ) { validation.addError( 'Incomplete repository location provided, workspace not provided', ); @@ -52,7 +59,7 @@ export const repoPickerValidation = ( 'Incomplete repository location provided, project not provided', ); } - } else if (integrationApi?.byHost(host)?.type === 'azure') { + } else if (integrationType === 'azure') { if (!searchParams.get('project')) { validation.addError( 'Incomplete repository location provided, project not provided', diff --git a/yarn.lock b/yarn.lock index 3f6c5e083f..3956198af0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6709,7 +6709,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-scaffolder-backend-module-bitbucket-cloud@workspace:^, @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@workspace:plugins/scaffolder-backend-module-bitbucket-cloud": +"@backstage/plugin-scaffolder-backend-module-bitbucket-cloud@workspace:plugins/scaffolder-backend-module-bitbucket-cloud": version: 0.0.0-use.local resolution: "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud@workspace:plugins/scaffolder-backend-module-bitbucket-cloud" dependencies: @@ -6730,7 +6730,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-scaffolder-backend-module-bitbucket-server@workspace:^, @backstage/plugin-scaffolder-backend-module-bitbucket-server@workspace:plugins/scaffolder-backend-module-bitbucket-server": +"@backstage/plugin-scaffolder-backend-module-bitbucket-server@workspace:plugins/scaffolder-backend-module-bitbucket-server": version: 0.0.0-use.local resolution: "@backstage/plugin-scaffolder-backend-module-bitbucket-server@workspace:plugins/scaffolder-backend-module-bitbucket-server" dependencies: @@ -6748,26 +6748,6 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-scaffolder-backend-module-bitbucket@workspace:plugins/scaffolder-backend-module-bitbucket": - version: 0.0.0-use.local - resolution: "@backstage/plugin-scaffolder-backend-module-bitbucket@workspace:plugins/scaffolder-backend-module-bitbucket" - dependencies: - "@backstage/backend-plugin-api": "workspace:^" - "@backstage/backend-test-utils": "workspace:^" - "@backstage/cli": "workspace:^" - "@backstage/config": "workspace:^" - "@backstage/errors": "workspace:^" - "@backstage/integration": "workspace:^" - "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "workspace:^" - "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "workspace:^" - "@backstage/plugin-scaffolder-node": "workspace:^" - "@backstage/plugin-scaffolder-node-test-utils": "workspace:^" - fs-extra: "npm:^11.2.0" - msw: "npm:^1.0.0" - yaml: "npm:^2.0.0" - languageName: unknown - linkType: soft - "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown@workspace:plugins/scaffolder-backend-module-confluence-to-markdown": version: 0.0.0-use.local resolution: "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown@workspace:plugins/scaffolder-backend-module-confluence-to-markdown"