diff --git a/.changeset/big-islands-add.md b/.changeset/big-islands-add.md new file mode 100644 index 0000000000..4a6511562b --- /dev/null +++ b/.changeset/big-islands-add.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Set the `searchTooltip` to "Filter" to follow how the `searchPlaceholder` is set making this more consistent diff --git a/.changeset/blue-lions-greet.md b/.changeset/blue-lions-greet.md deleted file mode 100644 index c4f4d2a337..0000000000 --- a/.changeset/blue-lions-greet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-user-settings': patch ---- - -Handle errors that may occur when the user logs out diff --git a/.changeset/brave-peaches-brush.md b/.changeset/brave-peaches-brush.md deleted file mode 100644 index f0661ba818..0000000000 --- a/.changeset/brave-peaches-brush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Making the description of the GitLab repoUrl owner field more clearer by focusing it refers to the GitLab namespace. diff --git a/.changeset/breezy-pots-worry.md b/.changeset/breezy-pots-worry.md deleted file mode 100644 index 48d5ae6c80..0000000000 --- a/.changeset/breezy-pots-worry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-org': patch ---- - -consistently show parent and child relations in group profile card diff --git a/.changeset/bright-rules-shout.md b/.changeset/bright-rules-shout.md deleted file mode 100644 index f7cdae3b4f..0000000000 --- a/.changeset/bright-rules-shout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/catalog-model': patch ---- - -Defer `ajv` compilation of schema validators to improve module-import performance diff --git a/.changeset/brown-days-pretend.md b/.changeset/brown-days-pretend.md new file mode 100644 index 0000000000..4b6a328724 --- /dev/null +++ b/.changeset/brown-days-pretend.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-tech-insights': patch +--- + +Fixed bug when sending data by Post in `runChecks` and `runBulkChecks` functions of the `TechInsightsClient` class, the default `Content-Type` used was `plain/text` diff --git a/.changeset/brown-grapes-battle.md b/.changeset/brown-grapes-battle.md deleted file mode 100644 index f94c938b02..0000000000 --- a/.changeset/brown-grapes-battle.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-bitbucket-cloud': minor ---- - -Bitbucket Cloud provider: Add option to configure schedule via `app-config.yaml` instead of in code. - -Please find how to configure the schedule at the config at -https://backstage.io/docs/integrations/bitbucketCloud/discovery diff --git a/.changeset/calm-moose-fetch.md b/.changeset/calm-moose-fetch.md deleted file mode 100644 index 96a364c801..0000000000 --- a/.changeset/calm-moose-fetch.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-catalog-react': minor -'@backstage/plugin-catalog': patch ---- - -Implemented the visual parts of `EntityKindPicker` so that it can be shown alongside the other filters on the left side of your catalog pages. diff --git a/.changeset/calm-pianos-burn.md b/.changeset/calm-pianos-burn.md deleted file mode 100644 index 6a44d42cfe..0000000000 --- a/.changeset/calm-pianos-burn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-react': patch ---- - -Added two new `EntityRefLinks` props, the first being `getTitle` that allows for customization of the title used for each link. The second one is `fetchEntities`, which triggers a fetching of all entities so that the full entity definition is available in the `getTitle` callback. diff --git a/.changeset/chatty-planets-flash.md b/.changeset/chatty-planets-flash.md new file mode 100644 index 0000000000..d6a5c6ad42 --- /dev/null +++ b/.changeset/chatty-planets-flash.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-gitlab': patch +--- + +Add `gitlabDiscoveryEntityProviderCatalogModule` (new backend-plugin-api, alpha). diff --git a/.changeset/clean-camels-sneeze.md b/.changeset/clean-camels-sneeze.md deleted file mode 100644 index 0fccb52e0b..0000000000 --- a/.changeset/clean-camels-sneeze.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Newly created Backstage repositories now use the stable version 6 of -`react-router`, just like the main repo does. Please let us know if you find any -issues with this. - -Migrating to the stable version of `react-router` is optional for the time -being. But if you want to do the same for your existing repository, please -follow [this -guide](https://backstage.io/docs/tutorials/react-router-stable-migration). diff --git a/.changeset/clean-planets-rhyme.md b/.changeset/clean-planets-rhyme.md new file mode 100644 index 0000000000..a6878b491b --- /dev/null +++ b/.changeset/clean-planets-rhyme.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-catalog-backend-module-azure': patch +--- + +`AzureDevOpsEntityProvider`: Add option to configure schedule via `app-config.yaml` instead of in code. + +Please find how to configure the schedule at the config at +https://backstage.io/docs/integrations/azure/discovery diff --git a/.changeset/clever-ties-burn.md b/.changeset/clever-ties-burn.md deleted file mode 100644 index cdd861e069..0000000000 --- a/.changeset/clever-ties-burn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-user-settings': patch ---- - -Prevent `.set()` to execute a request to the StorageClient if the user is `guest` diff --git a/.changeset/cuddly-bikes-tease.md b/.changeset/cuddly-bikes-tease.md deleted file mode 100644 index d3fc8bd1bb..0000000000 --- a/.changeset/cuddly-bikes-tease.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-app-api': patch ---- - -Fixed a bug where gathered index routes would fail to bind routable extensions. This would typically show up when placing a routable extension in the entity page overview tab. diff --git a/.changeset/curly-rats-itch.md b/.changeset/curly-rats-itch.md deleted file mode 100644 index b6fba9dc1e..0000000000 --- a/.changeset/curly-rats-itch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Tweak the Jest Caching loader to only operate when in `watch` mode diff --git a/.changeset/curvy-islands-marry.md b/.changeset/curvy-islands-marry.md new file mode 100644 index 0000000000..2d3bad31ac --- /dev/null +++ b/.changeset/curvy-islands-marry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-github-pull-requests-board': patch +--- + +Replace the momentjs dependency with luxon. diff --git a/.changeset/curvy-kiwis-fold.md b/.changeset/curvy-kiwis-fold.md deleted file mode 100644 index 8d94f38343..0000000000 --- a/.changeset/curvy-kiwis-fold.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Update versions of packages used in the create-app template, to match those in the main repo diff --git a/.changeset/curvy-lemons-change.md b/.changeset/curvy-lemons-change.md deleted file mode 100644 index 29e31a7b60..0000000000 --- a/.changeset/curvy-lemons-change.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-test-utils': patch ---- - -Fixed handling of root scoped services in `startTestBackend`. diff --git a/.changeset/dry-shirts-attack.md b/.changeset/dry-shirts-attack.md deleted file mode 100644 index ba6c7379f5..0000000000 --- a/.changeset/dry-shirts-attack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Support custom layouts in dry run editor diff --git a/.changeset/dull-rocks-warn.md b/.changeset/dull-rocks-warn.md deleted file mode 100644 index 0ccf4f20cb..0000000000 --- a/.changeset/dull-rocks-warn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': minor ---- - -Added a new property called `additionalTemplateGlobals` which allows you to add global functions to the scaffolder nunjucks templates. diff --git a/.changeset/eleven-apples-accept.md b/.changeset/eleven-apples-accept.md deleted file mode 100644 index f46bf4fe0c..0000000000 --- a/.changeset/eleven-apples-accept.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Deep-import LightAsync component to improve module-import speed diff --git a/.changeset/fair-tools-melt.md b/.changeset/fair-tools-melt.md deleted file mode 100644 index e63e4d5181..0000000000 --- a/.changeset/fair-tools-melt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-app-api': patch ---- - -Added possibility to configure index plugin of the HTTP router service. diff --git a/.changeset/fifty-berries-learn.md b/.changeset/fifty-berries-learn.md deleted file mode 100644 index 647246a482..0000000000 --- a/.changeset/fifty-berries-learn.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -'@backstage/plugin-catalog': minor ---- - -Added new column `Label` to `CatalogTable.columns`, this new column allows you make use of labels from metadata. -For example: category and visibility are type of labels associated with API entity illustrated below. - -YAML code snippet for API entity - -```yaml -apiVersion: backstage.io/v1alpha1 -kind: API -metadata: - name: sample-api - description: API for sample - links: - - url: http://localhost:8080/swagger-ui.html - title: Swagger UI - tags: - - http - labels: - category: legacy - visibility: protected -``` - -Consumers can customise columns to include label column and show in api-docs list - -```typescript -const columns = [ - CatalogTable.columns.createNameColumn({ defaultKind: 'API' }), - CatalogTable.columns.createLabelColumn('category', { title: 'Category' }), - CatalogTable.columns.createLabelColumn('visibility', { - title: 'Visibility', - defaultValue: 'public', - }), -]; -``` diff --git a/.changeset/flat-kangaroos-kiss.md b/.changeset/flat-kangaroos-kiss.md new file mode 100644 index 0000000000..a79c8621b1 --- /dev/null +++ b/.changeset/flat-kangaroos-kiss.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-catalog-backend-module-gitlab': patch +--- + +`GitlabDiscoveryEntityProvider`: Add option to configure schedule via `app-config.yaml` instead of in code. + +Please find how to configure the schedule at the config at +https://backstage.io/docs/integrations/gitlab/discovery diff --git a/.changeset/forty-jokes-lie.md b/.changeset/forty-jokes-lie.md new file mode 100644 index 0000000000..bd44898004 --- /dev/null +++ b/.changeset/forty-jokes-lie.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-openapi': patch +--- + +Added support to use the `UrlReaders` when `$ref` pointing to a URL. diff --git a/.changeset/fuzzy-dolls-give.md b/.changeset/fuzzy-dolls-give.md deleted file mode 100644 index 4de4d2e128..0000000000 --- a/.changeset/fuzzy-dolls-give.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-tasks': patch ---- - -Added new function `readTaskScheduleDefinitionFromConfig` to read `TaskScheduleDefinition` (aka. schedule) from the `Config`. diff --git a/.changeset/giant-pianos-repeat.md b/.changeset/giant-pianos-repeat.md deleted file mode 100644 index d83ce285e3..0000000000 --- a/.changeset/giant-pianos-repeat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch ---- - -Add `bitbucketCloudCatalogModule` (new backend-plugin-api, alpha). diff --git a/.changeset/gorgeous-onions-thank.md b/.changeset/gorgeous-onions-thank.md new file mode 100644 index 0000000000..8b41bfa5a0 --- /dev/null +++ b/.changeset/gorgeous-onions-thank.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-catalog-backend-module-aws': patch +--- + +`AwsS3EntityProvider`: Add option to configure schedule via `app-config.yaml` instead of in code. + +Please find how to configure the schedule at the config at +https://backstage.io/docs/integrations/aws-s3/discovery diff --git a/.changeset/great-numbers-reply.md b/.changeset/great-numbers-reply.md deleted file mode 100644 index b72b05debf..0000000000 --- a/.changeset/great-numbers-reply.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Fix BitBucket server integration diff --git a/.changeset/grumpy-pans-knock.md b/.changeset/grumpy-pans-knock.md deleted file mode 100644 index 74e81e7bec..0000000000 --- a/.changeset/grumpy-pans-knock.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@techdocs/cli': patch -'@backstage/plugin-techdocs-node': patch ---- - -fixing techdocs-cli Docker client creation - -Docker client does not need to be created when --no-docker -option is provided. - -If you had DOCKER_CERT_PATH environment variable defined -the Docker client was looking for certificates -and breaking techdocs-cli generate command even with --no-docker -option. diff --git a/.changeset/heavy-elephants-nail.md b/.changeset/heavy-elephants-nail.md new file mode 100644 index 0000000000..473e3eda9e --- /dev/null +++ b/.changeset/heavy-elephants-nail.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': minor +--- + +Deprecated the `homepage` config as the component that used it - `HomepageTimer` - has been removed and replaced by the `HeaderWorldClock` in the home plugin diff --git a/.changeset/hot-geese-vanish.md b/.changeset/hot-geese-vanish.md deleted file mode 100644 index b392c875c7..0000000000 --- a/.changeset/hot-geese-vanish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': minor ---- - -Google OAuth refresh tokens will now be revoked on logout by calling Google's API diff --git a/.changeset/itchy-paws-protect.md b/.changeset/itchy-paws-protect.md new file mode 100644 index 0000000000..a0c272e5e4 --- /dev/null +++ b/.changeset/itchy-paws-protect.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-azure': patch +--- + +Add `azureDevOpsEntityProviderCatalogModule` (new backend-plugin-api, alpha). diff --git a/.changeset/itchy-schools-run.md b/.changeset/itchy-schools-run.md deleted file mode 100644 index 6e46cabb15..0000000000 --- a/.changeset/itchy-schools-run.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-plugin-api': patch ---- - -Allow interfaces to be used for inferred option types. diff --git a/.changeset/kind-emus-juggle.md b/.changeset/kind-emus-juggle.md new file mode 100644 index 0000000000..2a99deef81 --- /dev/null +++ b/.changeset/kind-emus-juggle.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-aws': patch +--- + +Add `awsS3EntityProviderCatalogModule` (new backend-plugin-api, alpha). diff --git a/.changeset/kind-penguins-report.md b/.changeset/kind-penguins-report.md deleted file mode 100644 index 4023ea5976..0000000000 --- a/.changeset/kind-penguins-report.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@backstage/plugin-auth-backend': minor ---- - -CookieConfigurer can optionally return the `SameSite` cookie attribute. -CookieConfigurer now requires an additional argument `appOrigin` - the origin URL of the app - which is used to calculate the `SameSite` attribute. -defaultCookieConfigurer returns the `SameSite` attribute which defaults to `Lax`. In cases where an auth-backend is running on a different domain than the App, `SameSite=None` is used - but only for secure contexts. This is so that cookies can be included in third-party requests. - -OAuthAdapterOptions has been modified to require additional arguments, `baseUrl`, and `cookieConfigurer`. -OAuthAdapter now resolves cookie configuration using its supplied CookieConfigurer for each request to make sure that the proper attributes always are set. diff --git a/.changeset/large-dingos-juggle.md b/.changeset/large-dingos-juggle.md deleted file mode 100644 index 8566a0f305..0000000000 --- a/.changeset/large-dingos-juggle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-defaults': patch ---- - -Added root logger service to the set of default services. diff --git a/.changeset/large-jars-fry.md b/.changeset/large-jars-fry.md deleted file mode 100644 index 0da3f4302b..0000000000 --- a/.changeset/large-jars-fry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-bazaar': patch ---- - -Link to the user catalog entity of a member diff --git a/.changeset/lazy-beds-pull.md b/.changeset/lazy-beds-pull.md deleted file mode 100644 index 0d8be46576..0000000000 --- a/.changeset/lazy-beds-pull.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Updated fallback versions of dependencies in all templates. diff --git a/.changeset/lazy-eyes-switch.md b/.changeset/lazy-eyes-switch.md deleted file mode 100644 index f712f852ae..0000000000 --- a/.changeset/lazy-eyes-switch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-user-settings-backend': patch ---- - -Use `Response.status` instead of `.send(number)` diff --git a/.changeset/lazy-fireants-check.md b/.changeset/lazy-fireants-check.md deleted file mode 100644 index 234501b07a..0000000000 --- a/.changeset/lazy-fireants-check.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes': patch ---- - -Add useCustomResources react hook for fetching Kubernetes Custom Resources diff --git a/.changeset/little-hats-yell.md b/.changeset/little-hats-yell.md deleted file mode 100644 index 6063459d8d..0000000000 --- a/.changeset/little-hats-yell.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': minor ---- - -Update the `github:publish` action to allow passing whether pull -requests must be up to date with the default branch before merging. diff --git a/.changeset/little-roses-rule.md b/.changeset/little-roses-rule.md deleted file mode 100644 index 4acec131e6..0000000000 --- a/.changeset/little-roses-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Added a new `repo test` command. diff --git a/.changeset/loud-dots-sit.md b/.changeset/loud-dots-sit.md deleted file mode 100644 index fa6175358e..0000000000 --- a/.changeset/loud-dots-sit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-user-settings': patch ---- - -Update installation instructions diff --git a/.changeset/lovely-peaches-fold.md b/.changeset/lovely-peaches-fold.md deleted file mode 100644 index dcff35f4d7..0000000000 --- a/.changeset/lovely-peaches-fold.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-tech-insights': patch ---- - -making available the search for the last FACTS executed diff --git a/.changeset/lucky-cows-boil.md b/.changeset/lucky-cows-boil.md deleted file mode 100644 index a598b3da95..0000000000 --- a/.changeset/lucky-cows-boil.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/plugin-bitbucket-cloud-common': minor ---- - -Update Bitbucket Cloud models to latest OAS version. - -The latest specification contained some BREAKING CHANGES -due to removed fields. - -All of these fields are not used at other plugins, though. -Therefore, this change has no impact on other modules here. diff --git a/.changeset/mean-spiders-design.md b/.changeset/mean-spiders-design.md deleted file mode 100644 index 3704c9fca9..0000000000 --- a/.changeset/mean-spiders-design.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-search-backend': minor ---- - -Be less restrictive with unknown keys on query endpoint diff --git a/.changeset/metal-dogs-swim.md b/.changeset/metal-dogs-swim.md new file mode 100644 index 0000000000..d97c7077f2 --- /dev/null +++ b/.changeset/metal-dogs-swim.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-gerrit': patch +--- + +Add `gerritEntityProviderCatalogModule` (new backend-plugin-api, alpha). diff --git a/.changeset/mighty-lions-march.md b/.changeset/mighty-lions-march.md deleted file mode 100644 index 2f0b63d238..0000000000 --- a/.changeset/mighty-lions-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-vault-backend': patch ---- - -VaultBuilder.tsx renamed to VaultBuilder in order for module to be correctly loaded. diff --git a/.changeset/odd-pandas-suffer.md b/.changeset/odd-pandas-suffer.md deleted file mode 100644 index ce562a9a89..0000000000 --- a/.changeset/odd-pandas-suffer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-github-issues': patch ---- - -Updated the `luxon` dependency to 3.x diff --git a/.changeset/odd-singers-taste.md b/.changeset/odd-singers-taste.md deleted file mode 100644 index 524de42255..0000000000 --- a/.changeset/odd-singers-taste.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': minor ---- - -Add support for link to TechDocs and other links defined in template entity specification metadata on TemplateCard diff --git a/.changeset/old-cobras-suffer.md b/.changeset/old-cobras-suffer.md deleted file mode 100644 index 2891c29133..0000000000 --- a/.changeset/old-cobras-suffer.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/test-utils': patch -'@backstage/plugin-cicd-statistics': patch ---- - -Align on the version of `@material-ui/icons` used, to `^4.9.1` like other packages in the main repo diff --git a/.changeset/old-melons-bathe.md b/.changeset/old-melons-bathe.md deleted file mode 100644 index ac21377171..0000000000 --- a/.changeset/old-melons-bathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Fixed a bug where the `allowed*` values for the `RepoUrlPicker` would be reset on render. diff --git a/.changeset/old-needles-brake.md b/.changeset/old-needles-brake.md deleted file mode 100644 index eea2bf6539..0000000000 --- a/.changeset/old-needles-brake.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-github-issues': patch -'@backstage/plugin-github-pull-requests-board': patch ---- - -Properly filter on relations instead of the spec, when finding by owner diff --git a/.changeset/perfect-moose-drum.md b/.changeset/perfect-moose-drum.md deleted file mode 100644 index 833e5eeb35..0000000000 --- a/.changeset/perfect-moose-drum.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Updated lockfile parsing to have better support for Yarn 3. diff --git a/.changeset/plenty-kids-fetch.md b/.changeset/plenty-kids-fetch.md deleted file mode 100644 index 6a31b4db20..0000000000 --- a/.changeset/plenty-kids-fetch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/catalog-client': patch ---- - -Renamed argument in `validateEntity` from `location` to `locationRef` diff --git a/.changeset/plenty-laws-end.md b/.changeset/plenty-laws-end.md deleted file mode 100644 index e026ca3fca..0000000000 --- a/.changeset/plenty-laws-end.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Fixed an issue where `getClient()` for a `pluginId` would return different clients and not share them diff --git a/.changeset/poor-clouds-ring.md b/.changeset/poor-clouds-ring.md deleted file mode 100644 index 62ba3928a7..0000000000 --- a/.changeset/poor-clouds-ring.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Fixed a bug where entities provided without a location key would always replace existing entities, rather than updating them. diff --git a/.changeset/poor-moons-impress.md b/.changeset/poor-moons-impress.md new file mode 100644 index 0000000000..f441cba1f2 --- /dev/null +++ b/.changeset/poor-moons-impress.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-tech-insights': patch +--- + +Set content type to 'application/json' in TechInsightsClient diff --git a/.changeset/pre.json b/.changeset/pre.json index c5f8b30596..0bdcd4a8d9 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -2,257 +2,178 @@ "mode": "pre", "tag": "next", "initialVersions": { - "example-app": "0.2.75", - "@backstage/app-defaults": "1.0.6", - "example-backend": "0.2.75", - "@backstage/backend-app-api": "0.2.1", - "@backstage/backend-common": "0.15.1", - "@backstage/backend-defaults": "0.1.1", - "example-backend-next": "0.0.3", - "@backstage/backend-plugin-api": "0.1.2", - "@backstage/backend-tasks": "0.3.5", - "@backstage/backend-test-utils": "0.1.28", - "@backstage/catalog-client": "1.1.0", - "@backstage/catalog-model": "1.1.1", - "@backstage/cli": "0.19.0", + "example-app": "0.2.76", + "@backstage/app-defaults": "1.0.7", + "example-backend": "0.2.76", + "@backstage/backend-app-api": "0.2.2", + "@backstage/backend-common": "0.15.2", + "@backstage/backend-defaults": "0.1.2", + "example-backend-next": "0.0.4", + "@backstage/backend-plugin-api": "0.1.3", + "@backstage/backend-tasks": "0.3.6", + "@backstage/backend-test-utils": "0.1.29", + "@backstage/catalog-client": "1.1.1", + "@backstage/catalog-model": "1.1.2", + "@backstage/cli": "0.20.0", "@backstage/cli-common": "0.1.10", - "@backstage/codemods": "0.1.39", - "@backstage/config": "1.0.2", - "@backstage/config-loader": "1.1.4", - "@backstage/core-app-api": "1.1.0", - "@backstage/core-components": "0.11.1", - "@backstage/core-plugin-api": "1.0.6", - "@backstage/create-app": "0.4.31", - "@backstage/dev-utils": "1.0.6", + "@backstage/codemods": "0.1.40", + "@backstage/config": "1.0.3", + "@backstage/config-loader": "1.1.5", + "@backstage/core-app-api": "1.1.1", + "@backstage/core-components": "0.11.2", + "@backstage/core-plugin-api": "1.0.7", + "@backstage/create-app": "0.4.32", + "@backstage/dev-utils": "1.0.7", "e2e-test": "0.2.0", - "@backstage/errors": "1.1.1", - "@backstage/integration": "1.3.1", - "@backstage/integration-react": "1.1.4", + "@backstage/errors": "1.1.2", + "@backstage/integration": "1.3.2", + "@backstage/integration-react": "1.1.5", "@backstage/release-manifests": "0.0.6", - "@techdocs/cli": "1.2.1", - "techdocs-cli-embedded-app": "0.2.74", - "@backstage/test-utils": "1.2.0", + "@techdocs/cli": "1.2.2", + "techdocs-cli-embedded-app": "0.2.75", + "@backstage/test-utils": "1.2.1", "@backstage/theme": "0.2.16", "@backstage/types": "1.0.0", "@backstage/version-bridge": "1.0.1", - "@backstage/plugin-adr": "0.2.1", - "@backstage/plugin-adr-backend": "0.2.1", - "@backstage/plugin-adr-common": "0.2.1", - "@backstage/plugin-airbrake": "0.3.9", - "@backstage/plugin-airbrake-backend": "0.2.9", - "@backstage/plugin-allure": "0.1.25", - "@backstage/plugin-analytics-module-ga": "0.1.20", - "@backstage/plugin-apache-airflow": "0.2.2", - "@backstage/plugin-api-docs": "0.8.9", + "@backstage/plugin-adr": "0.2.2", + "@backstage/plugin-adr-backend": "0.2.2", + "@backstage/plugin-adr-common": "0.2.2", + "@backstage/plugin-airbrake": "0.3.10", + "@backstage/plugin-airbrake-backend": "0.2.10", + "@backstage/plugin-allure": "0.1.26", + "@backstage/plugin-analytics-module-ga": "0.1.21", + "@backstage/plugin-apache-airflow": "0.2.3", + "@backstage/plugin-api-docs": "0.8.10", "@backstage/plugin-api-docs-module-protoc-gen-doc": "0.1.0", - "@backstage/plugin-apollo-explorer": "0.1.2", - "@backstage/plugin-app-backend": "0.3.36", - "@backstage/plugin-auth-backend": "0.16.0", - "@backstage/plugin-auth-node": "0.2.5", - "@backstage/plugin-azure-devops": "0.2.0", - "@backstage/plugin-azure-devops-backend": "0.3.15", + "@backstage/plugin-apollo-explorer": "0.1.3", + "@backstage/plugin-app-backend": "0.3.37", + "@backstage/plugin-auth-backend": "0.17.0", + "@backstage/plugin-auth-node": "0.2.6", + "@backstage/plugin-azure-devops": "0.2.1", + "@backstage/plugin-azure-devops-backend": "0.3.16", "@backstage/plugin-azure-devops-common": "0.3.0", - "@backstage/plugin-badges": "0.2.33", - "@backstage/plugin-badges-backend": "0.1.30", - "@backstage/plugin-bazaar": "0.1.24", - "@backstage/plugin-bazaar-backend": "0.1.20", - "@backstage/plugin-bitbucket-cloud-common": "0.1.3", - "@backstage/plugin-bitrise": "0.1.36", - "@backstage/plugin-catalog": "1.5.1", - "@backstage/plugin-catalog-backend": "1.4.0", - "@backstage/plugin-catalog-backend-module-aws": "0.1.9", - "@backstage/plugin-catalog-backend-module-azure": "0.1.7", - "@backstage/plugin-catalog-backend-module-bitbucket": "0.2.3", - "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.1.3", - "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.1.1", - "@backstage/plugin-catalog-backend-module-gerrit": "0.1.4", - "@backstage/plugin-catalog-backend-module-github": "0.1.7", - "@backstage/plugin-catalog-backend-module-gitlab": "0.1.7", - "@backstage/plugin-catalog-backend-module-ldap": "0.5.3", - "@backstage/plugin-catalog-backend-module-msgraph": "0.4.2", - "@backstage/plugin-catalog-backend-module-openapi": "0.1.2", - "@backstage/plugin-catalog-common": "1.0.6", - "@internal/plugin-catalog-customized": "0.0.2", - "@backstage/plugin-catalog-graph": "0.2.21", - "@backstage/plugin-catalog-graphql": "0.3.13", - "@backstage/plugin-catalog-import": "0.8.12", - "@backstage/plugin-catalog-node": "1.1.0", - "@backstage/plugin-catalog-react": "1.1.4", - "@backstage/plugin-cicd-statistics": "0.1.11", - "@backstage/plugin-cicd-statistics-module-gitlab": "0.1.5", - "@backstage/plugin-circleci": "0.3.9", - "@backstage/plugin-cloudbuild": "0.3.9", - "@backstage/plugin-code-climate": "0.1.9", - "@backstage/plugin-code-coverage": "0.2.2", - "@backstage/plugin-code-coverage-backend": "0.2.2", - "@backstage/plugin-codescene": "0.1.4", - "@backstage/plugin-config-schema": "0.1.32", - "@backstage/plugin-cost-insights": "0.11.31", + "@backstage/plugin-badges": "0.2.34", + "@backstage/plugin-badges-backend": "0.1.31", + "@backstage/plugin-bazaar": "0.1.25", + "@backstage/plugin-bazaar-backend": "0.2.0", + "@backstage/plugin-bitbucket-cloud-common": "0.2.0", + "@backstage/plugin-bitrise": "0.1.37", + "@backstage/plugin-catalog": "1.6.0", + "@backstage/plugin-catalog-backend": "1.5.0", + "@backstage/plugin-catalog-backend-module-aws": "0.1.10", + "@backstage/plugin-catalog-backend-module-azure": "0.1.8", + "@backstage/plugin-catalog-backend-module-bitbucket": "0.2.4", + "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.1.4", + "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.1.2", + "@backstage/plugin-catalog-backend-module-gerrit": "0.1.5", + "@backstage/plugin-catalog-backend-module-github": "0.1.8", + "@backstage/plugin-catalog-backend-module-gitlab": "0.1.8", + "@backstage/plugin-catalog-backend-module-ldap": "0.5.4", + "@backstage/plugin-catalog-backend-module-msgraph": "0.4.3", + "@backstage/plugin-catalog-backend-module-openapi": "0.1.3", + "@backstage/plugin-catalog-common": "1.0.7", + "@internal/plugin-catalog-customized": "0.0.3", + "@backstage/plugin-catalog-graph": "0.2.22", + "@backstage/plugin-catalog-graphql": "0.3.14", + "@backstage/plugin-catalog-import": "0.9.0", + "@backstage/plugin-catalog-node": "1.2.0", + "@backstage/plugin-catalog-react": "1.2.0", + "@backstage/plugin-cicd-statistics": "0.1.12", + "@backstage/plugin-cicd-statistics-module-gitlab": "0.1.6", + "@backstage/plugin-circleci": "0.3.10", + "@backstage/plugin-cloudbuild": "0.3.10", + "@backstage/plugin-code-climate": "0.1.10", + "@backstage/plugin-code-coverage": "0.2.3", + "@backstage/plugin-code-coverage-backend": "0.2.3", + "@backstage/plugin-codescene": "0.1.5", + "@backstage/plugin-config-schema": "0.1.33", + "@backstage/plugin-cost-insights": "0.11.32", "@backstage/plugin-cost-insights-common": "0.1.1", - "@backstage/plugin-dynatrace": "0.2.0", - "@internal/plugin-todo-list": "1.0.5", - "@internal/plugin-todo-list-backend": "1.0.5", - "@internal/plugin-todo-list-common": "1.0.4", - "@backstage/plugin-explore": "0.3.40", - "@backstage/plugin-explore-react": "0.0.21", - "@backstage/plugin-firehydrant": "0.1.26", - "@backstage/plugin-fossa": "0.2.41", - "@backstage/plugin-gcalendar": "0.3.5", - "@backstage/plugin-gcp-projects": "0.3.28", - "@backstage/plugin-git-release-manager": "0.3.22", - "@backstage/plugin-github-actions": "0.5.9", - "@backstage/plugin-github-deployments": "0.1.40", - "@backstage/plugin-github-issues": "0.1.1", - "@backstage/plugin-github-pull-requests-board": "0.1.3", - "@backstage/plugin-gitops-profiles": "0.3.27", - "@backstage/plugin-gocd": "0.1.15", - "@backstage/plugin-graphiql": "0.2.41", - "@backstage/plugin-graphql-backend": "0.1.26", - "@backstage/plugin-home": "0.4.25", - "@backstage/plugin-ilert": "0.1.35", - "@backstage/plugin-jenkins": "0.7.8", - "@backstage/plugin-jenkins-backend": "0.1.26", - "@backstage/plugin-jenkins-common": "0.1.8", - "@backstage/plugin-kafka": "0.3.9", - "@backstage/plugin-kafka-backend": "0.2.29", - "@backstage/plugin-kubernetes": "0.7.2", - "@backstage/plugin-kubernetes-backend": "0.7.2", - "@backstage/plugin-kubernetes-common": "0.4.2", - "@backstage/plugin-lighthouse": "0.3.9", - "@backstage/plugin-newrelic": "0.3.27", - "@backstage/plugin-newrelic-dashboard": "0.2.2", - "@backstage/plugin-org": "0.5.9", - "@backstage/plugin-pagerduty": "0.5.2", - "@backstage/plugin-periskop": "0.1.7", - "@backstage/plugin-periskop-backend": "0.1.7", - "@backstage/plugin-permission-backend": "0.5.11", - "@backstage/plugin-permission-common": "0.6.4", - "@backstage/plugin-permission-node": "0.6.5", - "@backstage/plugin-permission-react": "0.4.5", - "@backstage/plugin-playlist": "0.1.0", - "@backstage/plugin-playlist-backend": "0.1.0", - "@backstage/plugin-playlist-common": "0.1.0", - "@backstage/plugin-proxy-backend": "0.2.30", - "@backstage/plugin-rollbar": "0.4.9", - "@backstage/plugin-rollbar-backend": "0.1.33", - "@backstage/plugin-scaffolder": "1.6.0", - "@backstage/plugin-scaffolder-backend": "1.6.0", - "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.11", - "@backstage/plugin-scaffolder-backend-module-rails": "0.4.4", - "@backstage/plugin-scaffolder-backend-module-yeoman": "0.2.9", - "@backstage/plugin-scaffolder-common": "1.2.0", - "@backstage/plugin-search": "1.0.2", - "@backstage/plugin-search-backend": "1.0.2", - "@backstage/plugin-search-backend-module-elasticsearch": "1.0.2", - "@backstage/plugin-search-backend-module-pg": "0.4.0", - "@backstage/plugin-search-backend-node": "1.0.2", - "@backstage/plugin-search-common": "1.0.1", - "@backstage/plugin-search-react": "1.1.0", - "@backstage/plugin-sentry": "0.4.2", - "@backstage/plugin-shortcuts": "0.3.1", - "@backstage/plugin-sonarqube": "0.4.1", - "@backstage/plugin-sonarqube-backend": "0.1.1", - "@backstage/plugin-splunk-on-call": "0.3.33", - "@backstage/plugin-stack-overflow": "0.1.5", - "@backstage/plugin-stack-overflow-backend": "0.1.5", - "@backstage/plugin-tech-insights": "0.3.0", - "@backstage/plugin-tech-insights-backend": "0.5.2", - "@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.20", - "@backstage/plugin-tech-insights-common": "0.2.6", - "@backstage/plugin-tech-insights-node": "0.3.4", - "@backstage/plugin-tech-radar": "0.5.16", - "@backstage/plugin-techdocs": "1.3.2", - "@backstage/plugin-techdocs-addons-test-utils": "1.0.4", - "@backstage/plugin-techdocs-backend": "1.3.0", - "@backstage/plugin-techdocs-module-addons-contrib": "1.0.4", - "@backstage/plugin-techdocs-node": "1.4.0", - "@backstage/plugin-techdocs-react": "1.0.4", - "@backstage/plugin-todo": "0.2.11", - "@backstage/plugin-todo-backend": "0.1.33", - "@backstage/plugin-user-settings": "0.4.8", - "@backstage/plugin-user-settings-backend": "0.1.0", - "@backstage/plugin-vault": "0.1.3", - "@backstage/plugin-vault-backend": "0.2.2", - "@backstage/plugin-xcmetrics": "0.2.29" + "@backstage/plugin-dynatrace": "1.0.0", + "@internal/plugin-todo-list": "1.0.6", + "@internal/plugin-todo-list-backend": "1.0.6", + "@internal/plugin-todo-list-common": "1.0.5", + "@backstage/plugin-explore": "0.3.41", + "@backstage/plugin-explore-react": "0.0.22", + "@backstage/plugin-firehydrant": "0.1.27", + "@backstage/plugin-fossa": "0.2.42", + "@backstage/plugin-gcalendar": "0.3.6", + "@backstage/plugin-gcp-projects": "0.3.29", + "@backstage/plugin-git-release-manager": "0.3.23", + "@backstage/plugin-github-actions": "0.5.10", + "@backstage/plugin-github-deployments": "0.1.41", + "@backstage/plugin-github-issues": "0.1.2", + "@backstage/plugin-github-pull-requests-board": "0.1.4", + "@backstage/plugin-gitops-profiles": "0.3.28", + "@backstage/plugin-gocd": "0.1.16", + "@backstage/plugin-graphiql": "0.2.42", + "@backstage/plugin-graphql-backend": "0.1.27", + "@backstage/plugin-home": "0.4.26", + "@backstage/plugin-ilert": "0.1.36", + "@backstage/plugin-jenkins": "0.7.9", + "@backstage/plugin-jenkins-backend": "0.1.27", + "@backstage/plugin-jenkins-common": "0.1.9", + "@backstage/plugin-kafka": "0.3.10", + "@backstage/plugin-kafka-backend": "0.2.30", + "@backstage/plugin-kubernetes": "0.7.3", + "@backstage/plugin-kubernetes-backend": "0.7.3", + "@backstage/plugin-kubernetes-common": "0.4.3", + "@backstage/plugin-lighthouse": "0.3.10", + "@backstage/plugin-newrelic": "0.3.28", + "@backstage/plugin-newrelic-dashboard": "0.2.3", + "@backstage/plugin-org": "0.5.10", + "@backstage/plugin-pagerduty": "0.5.3", + "@backstage/plugin-periskop": "0.1.8", + "@backstage/plugin-periskop-backend": "0.1.8", + "@backstage/plugin-permission-backend": "0.5.12", + "@backstage/plugin-permission-common": "0.7.0", + "@backstage/plugin-permission-node": "0.7.0", + "@backstage/plugin-permission-react": "0.4.6", + "@backstage/plugin-playlist": "0.1.1", + "@backstage/plugin-playlist-backend": "0.2.0", + "@backstage/plugin-playlist-common": "0.1.1", + "@backstage/plugin-proxy-backend": "0.2.31", + "@backstage/plugin-rollbar": "0.4.10", + "@backstage/plugin-rollbar-backend": "0.1.34", + "@backstage/plugin-scaffolder": "1.7.0", + "@backstage/plugin-scaffolder-backend": "1.7.0", + "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.12", + "@backstage/plugin-scaffolder-backend-module-rails": "0.4.5", + "@backstage/plugin-scaffolder-backend-module-yeoman": "0.2.10", + "@backstage/plugin-scaffolder-common": "1.2.1", + "@backstage/plugin-search": "1.0.3", + "@backstage/plugin-search-backend": "1.1.0", + "@backstage/plugin-search-backend-module-elasticsearch": "1.0.3", + "@backstage/plugin-search-backend-module-pg": "0.4.1", + "@backstage/plugin-search-backend-node": "1.0.3", + "@backstage/plugin-search-common": "1.1.0", + "@backstage/plugin-search-react": "1.2.0", + "@backstage/plugin-sentry": "0.4.3", + "@backstage/plugin-shortcuts": "0.3.2", + "@backstage/plugin-sonarqube": "0.4.2", + "@backstage/plugin-sonarqube-backend": "0.1.2", + "@backstage/plugin-splunk-on-call": "0.3.34", + "@backstage/plugin-stack-overflow": "0.1.6", + "@backstage/plugin-stack-overflow-backend": "0.1.6", + "@backstage/plugin-tech-insights": "0.3.1", + "@backstage/plugin-tech-insights-backend": "0.5.3", + "@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.21", + "@backstage/plugin-tech-insights-common": "0.2.7", + "@backstage/plugin-tech-insights-node": "0.3.5", + "@backstage/plugin-tech-radar": "0.5.17", + "@backstage/plugin-techdocs": "1.3.3", + "@backstage/plugin-techdocs-addons-test-utils": "1.0.5", + "@backstage/plugin-techdocs-backend": "1.4.0", + "@backstage/plugin-techdocs-module-addons-contrib": "1.0.5", + "@backstage/plugin-techdocs-node": "1.4.1", + "@backstage/plugin-techdocs-react": "1.0.5", + "@backstage/plugin-todo": "0.2.12", + "@backstage/plugin-todo-backend": "0.1.34", + "@backstage/plugin-user-settings": "0.5.0", + "@backstage/plugin-user-settings-backend": "0.1.1", + "@backstage/plugin-vault": "0.1.4", + "@backstage/plugin-vault-backend": "0.2.3", + "@backstage/plugin-xcmetrics": "0.2.30" }, - "changesets": [ - "blue-lions-greet", - "brave-peaches-brush", - "breezy-pots-worry", - "bright-rules-shout", - "calm-moose-fetch", - "calm-pianos-burn", - "clean-camels-sneeze", - "clever-ties-burn", - "cuddly-bikes-tease", - "curly-rats-itch", - "curvy-kiwis-fold", - "curvy-lemons-change", - "dry-shirts-attack", - "dull-rocks-warn", - "eleven-apples-accept", - "fair-tools-melt", - "fifty-berries-learn", - "grumpy-pans-knock", - "hot-geese-vanish", - "itchy-schools-run", - "kind-penguins-report", - "large-dingos-juggle", - "lazy-beds-pull", - "lazy-fireants-check", - "little-roses-rule", - "loud-dots-sit", - "lovely-peaches-fold", - "lucky-cows-boil", - "mean-spiders-design", - "mighty-lions-march", - "odd-pandas-suffer", - "odd-singers-taste", - "old-cobras-suffer", - "old-needles-brake", - "perfect-moose-drum", - "plenty-kids-fetch", - "plenty-laws-end", - "poor-clouds-ring", - "pretty-buttons-develop", - "quiet-dancers-jog", - "quiet-hats-kick", - "quiet-ligers-draw", - "rare-hotels-cough", - "red-dots-sleep", - "red-pants-rush", - "renovate-5da63a4", - "renovate-a02d90b", - "rich-carrots-reflect", - "rude-bulldogs-sleep", - "search-bobcats-love", - "search-cars-hide", - "search-clouds-begin", - "search-cycles-sniff", - "search-dull-planes-prove", - "search-rats-grin", - "selfish-turkeys-exist", - "shaggy-books-smell", - "silent-bees-repeat", - "silly-rules-join", - "sixty-items-nail", - "slow-mirrors-eat", - "smooth-tables-pull", - "soft-falcons-love", - "stupid-dragons-complain", - "sweet-insects-camp", - "swift-phones-cheat", - "tall-baboons-deliver", - "tender-drinks-drive", - "tender-parrots-peel", - "thick-kings-destroy", - "tiny-mails-bathe", - "tough-hairs-sparkle", - "twelve-melons-notice", - "warm-days-watch", - "wild-weeks-live", - "wise-ligers-scream", - "yellow-lemons-march", - "young-bugs-remember" - ] + "changesets": [] } diff --git a/.changeset/pretty-buttons-develop.md b/.changeset/pretty-buttons-develop.md deleted file mode 100644 index 7c686df250..0000000000 --- a/.changeset/pretty-buttons-develop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Auth0 provider now supports optional `connection` and `connectionScope` parameters to configure social identity providers. diff --git a/.changeset/purple-masks-travel.md b/.changeset/purple-masks-travel.md deleted file mode 100644 index 65d0a5fe9b..0000000000 --- a/.changeset/purple-masks-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-github': patch ---- - -Add missing config schema for the `GitHubEntityProvider`. diff --git a/.changeset/quiet-dancers-jog.md b/.changeset/quiet-dancers-jog.md deleted file mode 100644 index 2cca69103a..0000000000 --- a/.changeset/quiet-dancers-jog.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-gcalendar': patch ---- - -Fixed loader showing when user not signed in diff --git a/.changeset/quiet-hats-kick.md b/.changeset/quiet-hats-kick.md deleted file mode 100644 index a0bbab4cb1..0000000000 --- a/.changeset/quiet-hats-kick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-jenkins-backend': patch ---- - -Fixed a bug where `extraRequestHeaders` configuration was ignored. diff --git a/.changeset/quiet-ligers-draw.md b/.changeset/quiet-ligers-draw.md deleted file mode 100644 index 30f5845a54..0000000000 --- a/.changeset/quiet-ligers-draw.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-app-backend': patch ---- - -Added alpha plugin implementation for the new backend system. Available at `@backstage/plugin-app-backend/alpha`. diff --git a/.changeset/rare-cougars-cross.md b/.changeset/rare-cougars-cross.md deleted file mode 100644 index 3bfb31ae3d..0000000000 --- a/.changeset/rare-cougars-cross.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'@backstage/backend-common': patch -'@backstage/cli': patch -'@backstage/plugin-airbrake-backend': patch -'@backstage/plugin-badges-backend': patch -'@backstage/plugin-catalog-backend': patch -'@backstage/plugin-graphql-backend': patch -'@backstage/plugin-periskop-backend': patch -'@backstage/plugin-permission-backend': patch -'@backstage/plugin-rollbar-backend': patch -'@backstage/plugin-search-backend': patch -'@backstage/plugin-tech-insights-backend': patch -'@backstage/plugin-user-settings-backend': patch ---- - -Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` diff --git a/.changeset/rare-hotels-cough.md b/.changeset/rare-hotels-cough.md deleted file mode 100644 index 1a2395cf56..0000000000 --- a/.changeset/rare-hotels-cough.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-plugin-api': patch ---- - -Added documentation for `createBackendModule`, with guidelines for choosing a module ID. diff --git a/.changeset/red-dots-sleep.md b/.changeset/red-dots-sleep.md deleted file mode 100644 index 0accac9734..0000000000 --- a/.changeset/red-dots-sleep.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Switching off duplicated timestamp in case of logging via task logger in a custom action diff --git a/.changeset/red-pants-rush.md b/.changeset/red-pants-rush.md deleted file mode 100644 index d2d7a52237..0000000000 --- a/.changeset/red-pants-rush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': minor ---- - -Add `allowAutoMerge` option for `publish:github` action diff --git a/.changeset/renovate-5da63a4.md b/.changeset/renovate-5da63a4.md deleted file mode 100644 index 6d27c9e99f..0000000000 --- a/.changeset/renovate-5da63a4.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-api-docs': patch ---- - -Updated dependency `@asyncapi/react-component` to `1.0.0-next.43`. diff --git a/.changeset/renovate-a02d90b.md b/.changeset/renovate-a02d90b.md deleted file mode 100644 index 7bb69d9988..0000000000 --- a/.changeset/renovate-a02d90b.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-api-docs': patch ---- - -Updated dependency `@asyncapi/react-component` to `1.0.0-next.42`. diff --git a/.changeset/rich-carrots-reflect.md b/.changeset/rich-carrots-reflect.md deleted file mode 100644 index 13a67bcd56..0000000000 --- a/.changeset/rich-carrots-reflect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Switched the Jest transform for YAML files to use a custom one available at `@backstage/cli/config/jestYamlTransform.js`. diff --git a/.changeset/rude-bulldogs-sleep.md b/.changeset/rude-bulldogs-sleep.md deleted file mode 100644 index 8a2a0590b3..0000000000 --- a/.changeset/rude-bulldogs-sleep.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Allow Placeholder value to be any value, not only string. diff --git a/.changeset/search-bobcats-love.md b/.changeset/search-bobcats-love.md deleted file mode 100644 index f562961c1b..0000000000 --- a/.changeset/search-bobcats-love.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -'@backstage/plugin-search-react': minor ---- - -The search query state now has an optional `pageLimit` property that determines how many results will be requested per page, it defaults to 25. - -Examples: -_Basic_ - -```jsx - - {results => { - // Item rendering logic is omitted - }} - -``` - -_With context_ - -```jsx - - - {results => { - // Item rendering logic is omitted - }} - - -``` diff --git a/.changeset/search-cars-hide.md b/.changeset/search-cars-hide.md deleted file mode 100644 index b89662e90a..0000000000 --- a/.changeset/search-cars-hide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-search-common': minor ---- - -There is a new property called `pageLimit` on the `SearchQuery` interface that specifies how many results should be returned per page. diff --git a/.changeset/search-clouds-begin.md b/.changeset/search-clouds-begin.md deleted file mode 100644 index b3c6fa4b69..0000000000 --- a/.changeset/search-clouds-begin.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -'@backstage/plugin-search-react': minor ---- - -The `` component now accepts an optional property `disableRenderingWithNoResults` to disable rendering when no results are returned. -Possibility to provide a custom no results component if needed through the `noResultsComponent` property. - -Examples: - -_Rendering a custom no results component_ - -```jsx -} -/> -``` - -_Disable rendering when there are no results_ - -```jsx - -``` diff --git a/.changeset/search-cycles-sniff.md b/.changeset/search-cycles-sniff.md deleted file mode 100644 index 350e499a18..0000000000 --- a/.changeset/search-cycles-sniff.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-search-backend-node': patch -'@backstage/plugin-search-backend-module-pg': patch -'@backstage/plugin-search-backend-module-elasticsearch': patch ---- - -The search engine has been updated to take advantage of the `pageLimit` property on search queries. If none is provided, the search engine will continue to use its default value of 25 results per page. diff --git a/.changeset/search-dull-planes-prove.md b/.changeset/search-dull-planes-prove.md deleted file mode 100644 index f185207ede..0000000000 --- a/.changeset/search-dull-planes-prove.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -'@backstage/plugin-search-react': minor ---- - -The `` component now accepts an optional property `disableRenderingWithNoResults` to disable rendering when no results are returned. -Possibility to provide a custom no results component if needed through the `noResultsComponent` property. - -Examples: - -_Rendering a custom no results component_ - -```jsx -} - title="Documentation" - noResultsComponent={} -/> -``` - -_Disable rendering when there are no results_ - -```jsx -} - title="Documentation" - disableRenderingWithNoResults -/> -``` diff --git a/.changeset/search-rats-grin.md b/.changeset/search-rats-grin.md deleted file mode 100644 index 7b561d9c19..0000000000 --- a/.changeset/search-rats-grin.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@backstage/plugin-search-backend': minor ---- - -The query received by search engines now contains a property called `pageLimit`, it specifies how many results to return per page when sending a query request to the search backend. - -Example: -_Returns up to 30 results per page_ - -``` -GET /query?pageLimit=30 -``` - -The search backend validates the page limit and this value must not exceed 100, but it doesn't set a default value for the page limit parameter, it leaves it up to each search engine to set this, so Lunr, Postgres and Elastic Search set 25 results per page as a default value. diff --git a/.changeset/selfish-turkeys-exist.md b/.changeset/selfish-turkeys-exist.md deleted file mode 100644 index 3530b3a864..0000000000 --- a/.changeset/selfish-turkeys-exist.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-scaffolder': minor ---- - -EntityPickers now support flags to control when to include default namespace -in result diff --git a/.changeset/shaggy-books-smell.md b/.changeset/shaggy-books-smell.md deleted file mode 100644 index 3f8aaa389c..0000000000 --- a/.changeset/shaggy-books-smell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-tech-insights-node': patch ---- - -Updated package role to be `node-library`. diff --git a/.changeset/silent-bees-repeat.md b/.changeset/silent-bees-repeat.md deleted file mode 100644 index 4592d9a703..0000000000 --- a/.changeset/silent-bees-repeat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes-backend': patch ---- - -kubernetes service locator now take request context parameters diff --git a/.changeset/silly-rules-join.md b/.changeset/silly-rules-join.md deleted file mode 100644 index 88d3898317..0000000000 --- a/.changeset/silly-rules-join.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Initializes a git repository when creating an app using @packages/create-app diff --git a/.changeset/sixty-items-nail.md b/.changeset/sixty-items-nail.md deleted file mode 100644 index bf8f9b8161..0000000000 --- a/.changeset/sixty-items-nail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes': patch ---- - -Fix infinite call bug in `useCustomResources` hook diff --git a/.changeset/slimy-suns-learn.md b/.changeset/slimy-suns-learn.md deleted file mode 100644 index 4a924a7a18..0000000000 --- a/.changeset/slimy-suns-learn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Fixed setting default branch for Bitbucket Server diff --git a/.changeset/slow-mirrors-eat.md b/.changeset/slow-mirrors-eat.md deleted file mode 100644 index 6996a977d0..0000000000 --- a/.changeset/slow-mirrors-eat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Fix GitLab provider setup so that it supports GitLab installations with a path in the URL. diff --git a/.changeset/smooth-tables-pull.md b/.changeset/smooth-tables-pull.md deleted file mode 100644 index 2833ff5da7..0000000000 --- a/.changeset/smooth-tables-pull.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Switch to upfront resolution of `swc-loader` in Webpack config. diff --git a/.changeset/soft-falcons-love.md b/.changeset/soft-falcons-love.md deleted file mode 100644 index 56ed193501..0000000000 --- a/.changeset/soft-falcons-love.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/plugin-user-settings': minor ---- - -**BREAKING**: The `apiRef` passed to `ProviderSettingsItem` now needs to -implement `ProfileInfoApi & SessionApi`, rather than just the latter. This is -unlikely to have an effect on most users though, since the builtin auth -providers generally implement both. - -Fixed settings page showing providers as logged out when the user is using more -than one provider, and displayed some additional login information. diff --git a/.changeset/spicy-parents-lick.md b/.changeset/spicy-parents-lick.md new file mode 100644 index 0000000000..692745ed12 --- /dev/null +++ b/.changeset/spicy-parents-lick.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Include query parameters when navigating to relative links in documents diff --git a/.changeset/stupid-dragons-complain.md b/.changeset/stupid-dragons-complain.md deleted file mode 100644 index 44be8342bf..0000000000 --- a/.changeset/stupid-dragons-complain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-test-utils': patch ---- - -Increased test database max connection pool size to reduce the risk of resource exhaustion. diff --git a/.changeset/stupid-pens-occur.md b/.changeset/stupid-pens-occur.md new file mode 100644 index 0000000000..d8af9a447a --- /dev/null +++ b/.changeset/stupid-pens-occur.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': minor +--- + +**BREAKING**: Removed the `HomepageTimer` as it has been replaced by the `HeaderWorldClock` in the Home plugin and was deprecated over a year ago. diff --git a/.changeset/sweet-insects-camp.md b/.changeset/sweet-insects-camp.md deleted file mode 100644 index 4f91a196e8..0000000000 --- a/.changeset/sweet-insects-camp.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/cli': minor ---- - -**BREAKING**: Bumped `jest`, `jest-runtime`, and `jest-environment-jsdom` to v29. This is up from v27, so check out both the [v28](https://jestjs.io/docs/28.x/upgrading-to-jest28) and [v29](https://jestjs.io/docs/upgrading-to-jest29) (later [here](https://jestjs.io/docs/29.x/upgrading-to-jest29)) migration guides. - -Particular changes that where encountered in the main Backstage repo are: - -- The updated snapshot format. -- `jest.useFakeTimers('legacy')` is now `jest.useFakeTimers({ legacyFakeTimers: true })`. -- Error objects collected by `withLogCollector` from `@backstage/test-utils` are now objects with a `detail` property rather than a string. diff --git a/.changeset/swift-phones-cheat.md b/.changeset/swift-phones-cheat.md deleted file mode 100644 index 6d1653caa3..0000000000 --- a/.changeset/swift-phones-cheat.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Removed `tsx` and `jsx` as supported extensions in backend packages. For most -repos, this will not have any effect. But if you inadvertently had added some -`tsx`/`jsx` files to your backend package, you may now start to see `code: 'MODULE_NOT_FOUND'` errors when launching the backend locally. The reason for -this is that the offending files get ignored during transpilation. Hence, the -importing file can no longer find anything to import. - -The fix is to rename any `.tsx` files in your backend packages to `.ts` instead, -or `.jsx` to `.js`. diff --git a/.changeset/tall-baboons-deliver.md b/.changeset/tall-baboons-deliver.md deleted file mode 100644 index a69e52e31a..0000000000 --- a/.changeset/tall-baboons-deliver.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-user-settings-backend': patch ---- - -Fix wrong import statement in `README.md`. diff --git a/.changeset/tasty-colts-hug.md b/.changeset/tasty-colts-hug.md new file mode 100644 index 0000000000..e56d48a441 --- /dev/null +++ b/.changeset/tasty-colts-hug.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-azure-sites': minor +'@backstage/plugin-azure-sites-backend': minor +'@backstage/plugin-azure-sites-common': minor +--- + +Azure Sites (Apps & Functions) support for a given entity. View the current status of the site, quickly jump to site's Overview page, or Log Stream page. diff --git a/.changeset/tasty-scissors-tickle.md b/.changeset/tasty-scissors-tickle.md new file mode 100644 index 0000000000..745172df36 --- /dev/null +++ b/.changeset/tasty-scissors-tickle.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-catalog-backend-module-bitbucket-server': patch +--- + +`BitbucketServerEntityProvider`: Add option to configure schedule via `app-config.yaml` instead of in code. + +Please find how to configure the schedule at the config at +https://backstage.io/docs/integrations/bitbucketServer/discovery diff --git a/.changeset/ten-pens-draw.md b/.changeset/ten-pens-draw.md new file mode 100644 index 0000000000..be35a6f3b0 --- /dev/null +++ b/.changeset/ten-pens-draw.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-catalog-backend-module-gerrit': patch +--- + +`GerritEntityProvider`: Add option to configure schedule via `app-config.yaml` instead of in code. + +Please find how to configure the schedule at the config at +https://backstage.io/docs/integrations/gerrit/discovery diff --git a/.changeset/tender-drinks-drive.md b/.changeset/tender-drinks-drive.md deleted file mode 100644 index 0ea69832af..0000000000 --- a/.changeset/tender-drinks-drive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': minor ---- - -Add functionality to shutdown scaffolder tasks if they are stale diff --git a/.changeset/tender-parrots-peel.md b/.changeset/tender-parrots-peel.md deleted file mode 100644 index 566467e143..0000000000 --- a/.changeset/tender-parrots-peel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Added `*.session.sql` Visual Studio Code database functionality files to `.gitignore` in the default template. This is optional but potentially helpful if your developers use Visual Studio Code; you can add a line with that exact value to your own root `.gitignore` if you want the same. diff --git a/.changeset/thick-kings-destroy.md b/.changeset/thick-kings-destroy.md deleted file mode 100644 index 77c6be0d3c..0000000000 --- a/.changeset/thick-kings-destroy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Make sure to stitch entities correctly after deletion, to ensure that their relations are updated. diff --git a/.changeset/tiny-mails-bathe.md b/.changeset/tiny-mails-bathe.md deleted file mode 100644 index 868c2a8d36..0000000000 --- a/.changeset/tiny-mails-bathe.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Updated the root `test` scripts to use `backstage-cli repo test`. - -To apply this change to an existing app, make the following change to the root `package.json`: - -```diff -- "test": "backstage-cli test", -- "test:all": "lerna run test -- --coverage", -+ "test": "backstage-cli repo test", -+ "test:all": "backstage-cli repo test --coverage", -``` diff --git a/.changeset/tough-hairs-sparkle.md b/.changeset/tough-hairs-sparkle.md deleted file mode 100644 index 404657227b..0000000000 --- a/.changeset/tough-hairs-sparkle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Add support for `allowedOrganizations` and `allowedOwners` to the `AzureRepoPicker`. diff --git a/.changeset/tough-seahorses-learn.md b/.changeset/tough-seahorses-learn.md deleted file mode 100644 index 518b6b6967..0000000000 --- a/.changeset/tough-seahorses-learn.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -'@backstage/plugin-bazaar-backend': minor ---- - -**BREAKING** The bazaar-backend `createRouter` now requires that the `identityApi` is passed to the router. - -These changes are **required** to `packages/backend/src/plugins/bazaar.ts` - -The user entity ref is now added to the members table and is taken from the requesting user using the `identityApi`. - -```diff -import { PluginEnvironment } from '../types'; -import { createRouter } from '@backstage/plugin-bazaar-backend'; -import { Router } from 'express'; - -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - return await createRouter({ - logger: env.logger, - config: env.config, - database: env.database, -+ identity: env.identity, - }); -} -``` diff --git a/.changeset/twelve-melons-notice.md b/.changeset/twelve-melons-notice.md deleted file mode 100644 index 4a91642426..0000000000 --- a/.changeset/twelve-melons-notice.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-tech-insights-backend': patch ---- - -Modify router endpoint to handle singular and collections of request parameters similarly. diff --git a/.changeset/two-oranges-joke.md b/.changeset/two-oranges-joke.md new file mode 100644 index 0000000000..7f116e8ab0 --- /dev/null +++ b/.changeset/two-oranges-joke.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-bitbucket-server': patch +--- + +Add `bitbucketServerEntityProviderCatalogModule` (new backend-plugin-api, alpha). diff --git a/.changeset/unlucky-seas-sip.md b/.changeset/unlucky-seas-sip.md deleted file mode 100644 index 947e248244..0000000000 --- a/.changeset/unlucky-seas-sip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': minor ---- - -Added optional `sourcePath` parameter to `publish:gitlab:merge-request` action, `targetPath` is now optional and falls back to current workspace path. diff --git a/.changeset/warm-days-watch.md b/.changeset/warm-days-watch.md deleted file mode 100644 index b0a211c7a8..0000000000 --- a/.changeset/warm-days-watch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog': patch ---- - -Added support for copy entity URL in entity page context menu diff --git a/.changeset/wet-cameras-call.md b/.changeset/wet-cameras-call.md new file mode 100644 index 0000000000..3d58719269 --- /dev/null +++ b/.changeset/wet-cameras-call.md @@ -0,0 +1,10 @@ +--- +'@backstage/plugin-kubernetes-backend': minor +'@backstage/plugin-kubernetes-common': patch +--- + +The Kubernetes errors when fetching pod metrics are now captured and returned to the frontend. + +- **BREAKING** The method `fetchPodMetricsByNamespace` in the interface `KubernetesFetcher` is changed to `fetchPodMetricsByNamespaces`. It now accepts a set of namespace strings and returns `Promise`. +- Add the `PodStatusFetchResponse` to the `FetchResponse` union type. +- Add `NOT_FOUND` to the `KubernetesErrorTypes` union type, the HTTP error with status code 404 will be mapped to this error. diff --git a/.changeset/wild-weeks-live.md b/.changeset/wild-weeks-live.md deleted file mode 100644 index e66b3193d0..0000000000 --- a/.changeset/wild-weeks-live.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-scaffolder-backend': patch ---- - -Ignore .git directories in Template Editor, increase upload limit for dry-runs to 10MB. diff --git a/.changeset/wise-ligers-scream.md b/.changeset/wise-ligers-scream.md deleted file mode 100644 index d71c69fd42..0000000000 --- a/.changeset/wise-ligers-scream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cost-insights': patch ---- - -Fixed bug in `CostOverviewBreakdownChart` component where some datasets caused the cost overview breakdown chart to tear. diff --git a/.changeset/yellow-lemons-march.md b/.changeset/yellow-lemons-march.md deleted file mode 100644 index 24d9e760ad..0000000000 --- a/.changeset/yellow-lemons-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-app-api': patch ---- - -Properly export `rootLoggerFactory`. diff --git a/.changeset/young-bugs-remember.md b/.changeset/young-bugs-remember.md deleted file mode 100644 index 8f78495ed7..0000000000 --- a/.changeset/young-bugs-remember.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-github': patch -'@backstage/plugin-scaffolder-backend': patch ---- - -Updated the `moduleId` of the experimental module export. diff --git a/.changeset/young-olives-cheer.md b/.changeset/young-olives-cheer.md deleted file mode 100644 index 536a8d6370..0000000000 --- a/.changeset/young-olives-cheer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-github-issues': patch ---- - -Add filtering and ordering to the graphql query diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index cb1dd5d322..2dd8f4b55b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -35,6 +35,7 @@ yarn.lock @backstage/reviewers @backst /plugins/code-coverage @backstage/reviewers @alde @nissayeva /plugins/code-coverage-backend @backstage/reviewers @alde @nissayeva /plugins/cost-insights @backstage/reviewers @backstage/silver-lining +/plugins/cost-insights-* @backstage/reviewers @backstage/silver-lining /plugins/explore @backstage/reviewers @backstage/sda-se-reviewers /plugins/explore-react @backstage/reviewers @backstage/sda-se-reviewers /plugins/fossa @backstage/reviewers @backstage/sda-se-reviewers @@ -44,13 +45,15 @@ yarn.lock @backstage/reviewers @backst /plugins/ilert @backstage/reviewers @yacut /plugins/jenkins @backstage/reviewers @timja /plugins/jenkins-backend @backstage/reviewers @timja -/plugins/kafka @backstage/reviewers @nirga -/plugins/kafka-backend @backstage/reviewers @nirga +/plugins/kafka @backstage/reviewers @nirga @andrewthauer +/plugins/kafka-backend @backstage/reviewers @nirga @andrewthauer /plugins/kubernetes @backstage/reviewers @backstage/warpspeed /plugins/kubernetes-* @backstage/reviewers @backstage/warpspeed /plugins/newrelic-dashboard @backstage/reviewers @mufaddal7 /plugins/playlist @backstage/reviewers @kuangp /plugins/playlist-* @backstage/reviewers @kuangp +/plugins/rollbar @backstage/reviewers @andrewthauer +/plugins/rollbar-backend @backstage/reviewers @andrewthauer /plugins/scaffolder-backend-module-rails @backstage/reviewers @angeliski /plugins/scaffolder-backend-module-yeoman @backstage/reviewers @pawelmitka /plugins/search @backstage/reviewers @backstage/techdocs-core diff --git a/.github/renovate.json5 b/.github/renovate.json5 index a8bc3448a5..5c4745c70c 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -15,5 +15,10 @@ groupName: 'API Extractor / Rush Stack monorepo packages', rangeStrategy: 'replace', }, + { + matchSourceUrlPrefixes: ['https://github.com/gregberge/svgr'], + groupName: 'SVGR monorepo packages', + rangeStrategy: 'replace', + }, ], } diff --git a/.github/vale/Vocab/Backstage/accept.txt b/.github/vale/Vocab/Backstage/accept.txt index ae94912f38..bbfa748bb2 100644 --- a/.github/vale/Vocab/Backstage/accept.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -2,6 +2,7 @@ abc accessors addon addons +ADRs Airbrake Airbrakes airbrake @@ -28,6 +29,7 @@ bool boolean builtins callout +CDNs Chai changeset changesets @@ -68,6 +70,7 @@ dataflow dayjs debounce Debounce +debuggability declaratively deduplicated deps @@ -129,6 +132,7 @@ hotspots http https Iain +iLert img incentivised Indal @@ -139,6 +143,7 @@ JaCoCo JavaScript jenkins Jira +JWTs jq js json @@ -147,6 +152,7 @@ jsx Kaewkasi Keyv Knex +KPIs kubectl kubernetes kubernetes @@ -308,6 +314,7 @@ Superfences superset supertype storable +SVGs talkdesk Talkdesk tasklist @@ -352,17 +359,21 @@ utils validator validators varchar +VPCs +VSCode Wayfair Weaveworks Webpack winston www WWW +XCMetrics XML xyz yaml Zalando Zhou +zod zoomable zsh Alef diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index fb99f41c55..af6f222377 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -55,7 +55,7 @@ jobs: run: ls microsite/build/backstage && ls microsite/build/backstage/storybook - name: Deploy both microsite and storybook to gh-pages - uses: JamesIves/github-pages-deploy-action@v4.4.0 + uses: JamesIves/github-pages-deploy-action@v4.4.1 with: branch: gh-pages folder: microsite/build/backstage diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml index 46a6227f2c..089474e5b8 100644 --- a/.github/workflows/sync_snyk-github-issues.yml +++ b/.github/workflows/sync_snyk-github-issues.yml @@ -13,6 +13,9 @@ jobs: matrix: node-version: [14.x] + env: + NODE_OPTIONS: --max-old-space-size=4096 + steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/sync_version-packages.yml b/.github/workflows/sync_version-packages.yml index 5aa12fe6b5..22421925c7 100644 --- a/.github/workflows/sync_version-packages.yml +++ b/.github/workflows/sync_version-packages.yml @@ -4,6 +4,10 @@ on: branches: - master +concurrency: + group: sync-version-packages + cancel-in-progress: true + jobs: create-release-pr: name: Create Changeset PR diff --git a/ADOPTERS.md b/ADOPTERS.md index ca56a00890..bb17754c95 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -22,7 +22,7 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/ | [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | | [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | | [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | -| [Expedia Group](https://www.expediagroup.com) | [@guillermomanzo](https://github.com/guillermomanzo), [Sheena Sharma](mailto:shesharma@expediagroup.com), [@ajbw](https://github.com/ajbw) | EG Common Developer Toolkit | +| [Expedia Group](https://www.expediagroup.com) | [@gman0922](https://github.com/gman0922), [Sheena Sharma](mailto:shesharma@expediagroup.com), [Alekhya Karuturi](mailto:akaruturi@expediagroup.com) | EG Developer Front Door | | [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | | [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling | | [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. | @@ -71,7 +71,7 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/ | [Signal Iduna Group](https://www.signal-iduna.de/) | [Jonas Thomsen](https://github.com/JoThomsen) | Developer Portal, documentation, monitoring, service catalog for our insurance ecosystem | | [Tradeshift](https://www.tradeshift.com/) | [Soren Mathiasen](https://github.com/sorenmat) | Developer Portal: documentation, monitoring, service templates, service catalog for our micro services | | [Unity](https://unity.com) | [Ted Cordery](https://github.com/TeddyBallGame) | A centralized service catalog with documentation for our service engineers. | -| [PicPay](https://www.picpay.com) | [Luis Baroni](https://github.com/lcsbaroni), [Renata Poluceno](https://github.com/renatapoluceno), [PicPay](https://github.com/picpay) | Developer portal for building services through templates, service catalog with ownership of services, documentation and metrics providing autonomy and visibility for all. | +| [PicPay](https://www.picpay.com) | [Elton Welsch](https://github.com/eltonwelsch), [Emanuella Okada](https://github.com/ManuOkadaPicPay), [PicPay](https://github.com/picpay) | Developer portal for building services through templates, service catalog with ownership of services, documentation and metrics providing autonomy and visibility for all. | | [Epic Games](https://www.epicgames.com) | [Brian Jung](https://github.com/brian-at-epic), [Jeff Goldian](https://github.com/jeffgoldian-Epic) | Developer Portal: Service Catalog, Documentation, Software Templates and more making our internal teams' lives easier! | | [Globo](https://globo.com) | [Carlos Gusmão](https://github.com/caeugusmao), [Guilherme Vierno](https://github.com/vierno), [Denis Aoki](https://github.com/dnsaoki2), [Maycon Dionisio](https://github.com/MayconDionisio), | Reduce the friction of accessing the information engineers need about Globo's digital services through a coherent and centralized experience. | | [QBE](https://www.qbe.com/) | [Daniel Steel](https://github.com/danielsteelqbe), [Pete Jespers](https://github.com/petejespersqbe) | Developer portal allowing our global teams to explore and create applications, documentation and cloud infrastructure easily and quickly 🚀 | diff --git a/README.md b/README.md index 5dba1fdf52..53f8275b88 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![headline](docs/assets/headline.png) +[![headline](docs/assets/headline.png)](https://backstage.io/) # [Backstage](https://backstage.io) diff --git a/app-config.yaml b/app-config.yaml index 57349d213a..977b07612f 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -420,16 +420,6 @@ costInsights: kind: 'PINTS_OF_ICE_CREAM' unit: 'ice cream pint' rate: 5.5 -homepage: - clocks: - - label: UTC - timezone: UTC - - label: NYC - timezone: 'America/New_York' - - label: STO - timezone: 'Europe/Stockholm' - - label: TYO - timezone: 'Asia/Tokyo' pagerduty: eventsBaseUrl: 'https://events.pagerduty.com/v2' jenkins: diff --git a/contrib/docker/cookiecutter-with-jinja2-extensions/README.md b/contrib/docker/cookiecutter-with-jinja2-extensions/README.md index 0b754fa4ce..d51b46cb96 100644 --- a/contrib/docker/cookiecutter-with-jinja2-extensions/README.md +++ b/contrib/docker/cookiecutter-with-jinja2-extensions/README.md @@ -12,7 +12,7 @@ Using Cookiecutter extensions is a two-step process: This step depends on how the scaffolder is setup to use Cookiecutter: - Using a local Cookiecutter, or -- Using a Cookiecutter Docker image, e.g. [spotify/backstage-cookiecutter](https://github.com/backstage/backstage/blob/37e35b91/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile). +- Using a Cookiecutter Docker image, e.g. [spotify/backstage-cookiecutter](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile). Say we want to install [`jinja2_custom_filters_extension`](https://pypi.org/project/jinja2-custom-filters-extension/) to use the `upper_case_first_letter` filter in a Cookiecutter template. diff --git a/contrib/scripts/orphan-clean-up/README.md b/contrib/scripts/orphan-clean-up/README.md index a6a25a4e7d..6fdf51bbbf 100644 --- a/contrib/scripts/orphan-clean-up/README.md +++ b/contrib/scripts/orphan-clean-up/README.md @@ -2,20 +2,47 @@ ## Overview -The Orphan Clean Up script is a basic PowerShell script to delete orphaned entities in the catalog. This script also assumes that you do not have authentication setup for your Backstage API endpoints. +The Orphan Clean Up scripts are a basic scripts to delete orphaned entities in the catalog. + +This script also assumes that you do not have authentication setup for your Backstage API endpoints. _Warning:_ There is a risk of entities being orphaned (and being deleted by this script) in case of the location having problems and returning a 404 status code. This might lead to accidental deletion of entities until the processing loop has recreated the entity. -## Requirements +## PowerShell + +A PowerShell implementation of the orphan cleanup script. + +### Requirements This script is PowerShell based so therefore needs to be ran in a PowerShell session. If you are not able to use PowerShell the script should give you a clear idea as to how to create it with another scripting language like Bash. -## Usage +### Usage Here's how to use the script: -1. Download the script +1. Download the `OrphanCleanUp.ps1` script 2. Now start a PowerShell session 3. Next navigate to the location you downloaded the script 4. Then run this command replacing `https:\\backstage.my-company.com` with the URL of your Backstage instance: `.\OrphanCleanUp.ps1 https:\\backstage.my-company.com` 5. The script will output the number of orphaned entities it finds and then the name for each one it deletes + +## Bash + +A bash implementation of the orphan cleanup script. + +### Requirements + +This script is shell based and requires the following programs to be installed on your system: + +- curl +- jq + +### Usage + +Here's how to use the script: + +1. Download the `orphan_cleanup.sh` script +2. Now start a bash session +3. Next navigate to the location you downloaded the script +4. Then run this command replacing `https://backstage.my-company.com` with the URL of your Backstage instance: `./orphan_cleanup.sh https://backstage.my-company.com` +5. The script will output the number of orphaned entities it finds and then the name for each one it deletes diff --git a/contrib/scripts/orphan-clean-up/orphan_cleanup.sh b/contrib/scripts/orphan-clean-up/orphan_cleanup.sh new file mode 100755 index 0000000000..6ffe4737fb --- /dev/null +++ b/contrib/scripts/orphan-clean-up/orphan_cleanup.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -euo pipefail + +# Cleanes up orphaned entities for the provided Backstage URL, defaults to the local backend +BACKSTAGE_URL=${1:-'http://localhost:7007'} +echo $BACKSTAGE_URL + +ORPHAN_API_URL="$BACKSTAGE_URL/api/catalog/entities?filter=metadata.annotations.backstage.io/orphan=true" +ORPHAN_DELETE_API_URL="$BACKSTAGE_URL/api/catalog/entities/by-uid" + +ORPHANS=$(curl -s $ORPHAN_API_URL) + +echo "" +echo "Found $(echo $ORPHANS | jq length ) orphaned entities" +echo "" + +jq -c '.[]' <<< $ORPHANS | while read ORPHAN; do + echo $ORPHAN | jq "." + echo "Deleting orphan entity: $(echo $ORPHAN | jq -r .metadata.name) of kind: $(echo $ORPHAN | jq -r .kind)" + curl -X DELETE "$ORPHAN_DELETE_API_URL/$(echo $ORPHAN | jq -r .metadata.uid)" +done diff --git a/docs/assets/getting-started/portal.png b/docs/assets/getting-started/portal.png index f16ad44757..b3133f2278 100644 Binary files a/docs/assets/getting-started/portal.png and b/docs/assets/getting-started/portal.png differ diff --git a/docs/assets/getting-started/wizard.png b/docs/assets/getting-started/wizard.png index 679878e252..8b6dce6131 100644 Binary files a/docs/assets/getting-started/wizard.png and b/docs/assets/getting-started/wizard.png differ diff --git a/docs/auth/oidc.md b/docs/auth/oidc.md index f856a2a415..d915262eb7 100644 --- a/docs/auth/oidc.md +++ b/docs/auth/oidc.md @@ -243,8 +243,8 @@ need this as well) to support user sessions. ### The Sign In provider The last step is to add the provider to the `SignInPage` so users can sign in with your -new provider, please follow the [Sing In Configuration][3] docs, here's where you import -and use the API ref we defined earlier. +new provider, please follow the [Sign In Configuration][3] docs, here's where you import +and use the API reference we defined earlier. ## Note diff --git a/docs/conf/defining.md b/docs/conf/defining.md index 6462f4f60a..29964dbcbc 100644 --- a/docs/conf/defining.md +++ b/docs/conf/defining.md @@ -23,6 +23,17 @@ Each package is searched for a schema at a single point of entry, a top-level inlined JSON schema, or a relative path to a schema file. Supported schema file formats are `.json` or `.d.ts`. +```jsonc title="package.json" +{ + // ... + "files": [ + // ... + "config.d.ts" + ], + "configSchema": "config.d.ts" +} +``` + > When defining a schema file, be sure to include the file in your > `package.json` > `"files"` field as well! diff --git a/docs/deployment/docker.md b/docs/deployment/docker.md index d35e7dd593..04a509d316 100644 --- a/docs/deployment/docker.md +++ b/docs/deployment/docker.md @@ -61,13 +61,18 @@ FROM node:16-bullseye-slim # Install sqlite3 dependencies. You can skip this if you don't use sqlite3 in the image, # in which case you should also move better-sqlite3 to "devDependencies" in package.json. -RUN apt-get update && \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update && \ apt-get install -y --no-install-recommends libsqlite3-dev python3 build-essential && \ - rm -rf /var/lib/apt/lists/* && \ yarn config set python /usr/bin/python3 # From here on we use the least-privileged `node` user to run the backend. USER node + +# This should create the app dir as `node`. +# If it is instead created as `root` then the `tar` command below will fail: `can't create directory 'packages/': Permission denied`. +# If this occurs, then ensure BuildKit is enabled (`DOCKER_BUILDKIT=1`) so the app dir is correctly created as `node`. WORKDIR /app # This switches many Node.js dependencies to production mode. @@ -79,7 +84,8 @@ ENV NODE_ENV production COPY --chown=node:node yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./ RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz -RUN yarn install --frozen-lockfile --production --network-timeout 300000 && rm -rf "$(yarn cache dir)" +RUN --mount=type=cache,target=/home/node/.cache/yarn,sharing=locked,uid=1000,gid=1000 \ + yarn install --frozen-lockfile --production --network-timeout 300000 # Then copy the rest of the backend bundle, along with any other files we might want. COPY --chown=node:node packages/backend/dist/bundle.tar.gz app-config*.yaml ./ @@ -163,53 +169,68 @@ RUN find packages \! -name "package.json" -mindepth 2 -maxdepth 2 -exec rm -rf { # Stage 2 - Install dependencies and build packages FROM node:16-bullseye-slim AS build -WORKDIR /app -COPY --from=packages /app . - # install sqlite3 dependencies -RUN apt-get update && \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update && \ apt-get install -y --no-install-recommends libsqlite3-dev python3 build-essential && \ yarn config set python /usr/bin/python3 -RUN yarn install --frozen-lockfile --network-timeout 600000 && rm -rf "$(yarn cache dir)" +USER node +WORKDIR /app -COPY . . +COPY --from=packages --chown=node:node /app . + +# Stop cypress from downloading it's massive binary. +ENV CYPRESS_INSTALL_BINARY=0 +RUN --mount=type=cache,target=/home/node/.cache/yarn,sharing=locked,uid=1000,gid=1000 \ + yarn install --frozen-lockfile --network-timeout 600000 + +COPY --chown=node:node . . RUN yarn tsc RUN yarn --cwd packages/backend build # If you have not yet migrated to package roles, use the following command instead: # RUN yarn --cwd packages/backend backstage-cli backend:bundle --build-dependencies +RUN mkdir packages/backend/dist/skeleton packages/backend/dist/bundle \ + && tar xzf packages/backend/dist/skeleton.tar.gz -C packages/backend/dist/skeleton \ + && tar xzf packages/backend/dist/bundle.tar.gz -C packages/backend/dist/bundle + # Stage 3 - Build the actual backend image and install production dependencies FROM node:16-bullseye-slim # Install sqlite3 dependencies. You can skip this if you don't use sqlite3 in the image, # in which case you should also move better-sqlite3 to "devDependencies" in package.json. -RUN apt-get update && \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update && \ apt-get install -y --no-install-recommends libsqlite3-dev python3 build-essential && \ - rm -rf /var/lib/apt/lists/* && \ yarn config set python /usr/bin/python3 # From here on we use the least-privileged `node` user to run the backend. USER node + +# This should create the app dir as `node`. +# If it is instead created as `root` then the `tar` command below will fail: `can't create directory 'packages/': Permission denied`. +# If this occurs, then ensure BuildKit is enabled (`DOCKER_BUILDKIT=1`) so the app dir is correctly created as `node`. WORKDIR /app -# This switches many Node.js dependencies to production mode. -ENV NODE_ENV production - # Copy the install dependencies from the build stage and context -COPY --from=build --chown=node:node /app/yarn.lock /app/package.json /app/packages/backend/dist/skeleton.tar.gz ./ -RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz +COPY --from=build --chown=node:node /app/yarn.lock /app/package.json /app/packages/backend/dist/skeleton/ ./ -RUN yarn install --frozen-lockfile --production --network-timeout 600000 && rm -rf "$(yarn cache dir)" +RUN --mount=type=cache,target=/home/node/.cache/yarn,sharing=locked,uid=1000,gid=1000 \ + yarn install --frozen-lockfile --production --network-timeout 600000 # Copy the built packages from the build stage -COPY --from=build --chown=node:node /app/packages/backend/dist/bundle.tar.gz . -RUN tar xzf bundle.tar.gz && rm bundle.tar.gz +COPY --from=build --chown=node:node /app/packages/backend/dist/bundle/ ./ # Copy any other files that we need at runtime COPY --chown=node:node app-config.yaml ./ +# This switches many Node.js dependencies to production mode. +ENV NODE_ENV production + CMD ["node", "packages/backend", "--config", "app-config.yaml"] ``` @@ -225,6 +246,7 @@ however ignore any existing build output or dependencies on the host. For our new `.dockerignore`, replace the contents of your existing one with this: ```text +dist-types node_modules packages/*/dist packages/*/node_modules diff --git a/docs/features/kubernetes/configuration.md b/docs/features/kubernetes/configuration.md index 1dd684db5f..7dc0e326fa 100644 --- a/docs/features/kubernetes/configuration.md +++ b/docs/features/kubernetes/configuration.md @@ -62,7 +62,7 @@ This is an array used to determine where to retrieve cluster configuration from. Valid cluster locator methods are: - [`catalog`](#catalog) -- [`localKubectlProxy`](#localKubectlProxy) +- [`localKubectlProxy`](#localkubectlproxy) - [`config`](#config) - [`gke`](#gke) - [custom `KubernetesClustersSupplier`](#custom-kubernetesclusterssupplier) diff --git a/docs/features/software-catalog/well-known-annotations.md b/docs/features/software-catalog/well-known-annotations.md index 46f28dcb01..0a4f5ee400 100644 --- a/docs/features/software-catalog/well-known-annotations.md +++ b/docs/features/software-catalog/well-known-annotations.md @@ -110,7 +110,7 @@ metadata: backstage.io/edit-url: https://github.com/my-org/catalog/edit/master/my-service.jsonnet ``` -These annotations allow customising links from the catalog pages. The view URL +These annotations allow customizing links from the catalog pages. The view URL should point to the canonical metadata YAML that governs this entity. The edit URL should point to the source file for the metadata. In the example above, `my-org` generates its catalog data from Jsonnet files in a monorepo, so the @@ -159,11 +159,11 @@ metadata: github.com/project-slug: backstage/backstage ``` -The value of this annotation is the so-called slug that identifies a project on +The value of this annotation is the so-called slug that identifies a repository on [GitHub](https://github.com) (either the public one, or a private GitHub Enterprise installation) that is related to this entity. It is on the format -`/`, and is the same as can be seen in the URL location -bar of the browser when viewing that project. +`/`, and is the same as can be seen in the URL location +bar of the browser when viewing that repository. Specifying this annotation will enable GitHub related features in Backstage for that entity. diff --git a/docs/features/software-templates/writing-custom-field-extensions.md b/docs/features/software-templates/writing-custom-field-extensions.md index afef2c3372..b4c4d2d4ce 100644 --- a/docs/features/software-templates/writing-custom-field-extensions.md +++ b/docs/features/software-templates/writing-custom-field-extensions.md @@ -81,8 +81,7 @@ import { scaffolderPlugin, createScaffolderFieldExtension, } from '@backstage/plugin-scaffolder'; -import { MyCustomExtension } from './MyCustomExtension'; -import { myCustomValidation } from './validation'; +import { MyCustomExtension, myCustomValidation } from './MyCustomExtension'; export const MyCustomFieldExtension = scaffolderPlugin.provide( createScaffolderFieldExtension({ diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index ade73d3f68..477467bb88 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -51,14 +51,10 @@ create a subdirectory inside your current working directory. npx @backstage/create-app ``` -The wizard will ask you - -- The name of the app, which will also be the name of the directory -- The database type to use for the backend. For this guide, you'll be using the - SQLite option. +The wizard will ask you for the name of the app, which will also be the name of the directory

- Screenshot of the wizard asking for a name for the app, and a selection menu for the database. + Screenshot of the wizard asking for a name for the app.

### Run the Backstage app diff --git a/docs/integrations/aws-s3/discovery.md b/docs/integrations/aws-s3/discovery.md index 9e9e6ebede..748aeb2392 100644 --- a/docs/integrations/aws-s3/discovery.md +++ b/docs/integrations/aws-s3/discovery.md @@ -32,6 +32,11 @@ catalog: bucketName: sample-bucket prefix: prefix/ # optional region: us-east-2 # optional, uses the default region otherwise + schedule: # optional; same options as in TaskScheduleDefinition + # supports cron, ISO duration, "human duration" as used in code + frequency: { minutes: 30 } + # supports ISO duration, "human duration" as used in code + timeout: { minutes: 3 } ``` For simple setups, you can omit the provider ID at the config @@ -47,6 +52,11 @@ catalog: bucketName: sample-bucket prefix: prefix/ # optional region: us-east-2 # optional, uses the default region otherwise + schedule: # optional; same options as in TaskScheduleDefinition + # supports cron, ISO duration, "human duration" as used in code + frequency: { minutes: 30 } + # supports ISO duration, "human duration" as used in code + timeout: { minutes: 3 } ``` As this provider is not one of the default providers, you will first need to install @@ -69,10 +79,13 @@ const builder = await CatalogBuilder.create(env); builder.addEntityProvider( AwsS3EntityProvider.fromConfig(env.config, { logger: env.logger, + // optional: alternatively, use scheduler with schedule defined in app-config.yaml schedule: env.scheduler.createScheduledTaskRunner({ frequency: { minutes: 30 }, timeout: { minutes: 3 }, }), + // optional: alternatively, use schedule + scheduler: env.scheduler, }), ); ``` diff --git a/docs/integrations/azure/discovery.md b/docs/integrations/azure/discovery.md index 6a420462c0..2156f60814 100644 --- a/docs/integrations/azure/discovery.md +++ b/docs/integrations/azure/discovery.md @@ -42,12 +42,17 @@ catalog: project: myproject repository: service-* # this will match all repos starting with service-* path: /catalog-info.yaml + schedule: # optional; same options as in TaskScheduleDefinition + # supports cron, ISO duration, "human duration" as used in code + frequency: { minutes: 30 } + # supports ISO duration, "human duration" as used in code + timeout: { minutes: 3 } anotherProviderId: # another identifier organization: myorg project: myproject repository: '*' # this will match all repos path: /src/*/catalog-info.yaml # this will search for files deep inside the /src folder - yetAotherProviderId: # guess, what? Another one :) + yetAnotherProviderId: # guess, what? Another one :) host: selfhostedazure.yourcompany.com organization: myorg project: myproject @@ -55,11 +60,20 @@ catalog: The parameters available are: -- `host:` Leave empty for Cloud hosted, otherwise set to your self-hosted instance host. -- `organization:` Your Organization slug (or Collection for on-premise users). Required. -- `project:` Your project slug. Required. -- `repository:` The repository name. Wildcards are supported as show on the examples above. If not set, all repositories will be searched. -- `path:` Where to find catalog-info.yaml files. Defaults to /catalog-info.yaml. +- **`host:`** _(optional)_ Leave empty for Cloud hosted, otherwise set to your self-hosted instance host. +- **`organization:`** Your Organization slug (or Collection for on-premise users). Required. +- **`project:`** Your project slug. Required. +- **`repository:`** _(optional)_ The repository name. Wildcards are supported as show on the examples above. If not set, all repositories will be searched. +- **`path:`** _(optional)_ Where to find catalog-info.yaml files. Defaults to /catalog-info.yaml. +- **`schedule`** _(optional)_: + - **`frequency`**: + How often you want the task to run. The system does its best to avoid overlapping invocations. + - **`timeout`**: + The maximum amount of time that a single task invocation can take. + - **`initialDelay`** _(optional)_: + The amount of time that should pass before the first invocation happens. + - **`scope`** _(optional)_: + `'global'` or `'local'`. Sets the scope of concurrency control. _Note:_ the path parameter follows the same rules as the search on Azure DevOps web interface. For more details visit the @@ -84,10 +98,13 @@ const builder = await CatalogBuilder.create(env); +builder.addEntityProvider( + AzureDevOpsEntityProvider.fromConfig(env.config, { + logger: env.logger, ++ // optional: alternatively, use scheduler with schedule defined in app-config.yaml + schedule: env.scheduler.createScheduledTaskRunner({ -+ frequency: Duration.fromObject({ minutes: 30 }), -+ timeout: Duration.fromObject({ minutes: 3 }), ++ frequency: { minutes: 30 }, ++ timeout: { minutes: 3 }, + }), ++ // optional: alternatively, use schedule ++ scheduler: env.scheduler, + }), +); ``` diff --git a/docs/integrations/bitbucketServer/discovery.md b/docs/integrations/bitbucketServer/discovery.md index 57e52d1dae..c4288b33ec 100644 --- a/docs/integrations/bitbucketServer/discovery.md +++ b/docs/integrations/bitbucketServer/discovery.md @@ -37,10 +37,13 @@ And then add the entity provider to your catalog builder: + builder.addEntityProvider( + BitbucketServerEntityProvider.fromConfig(env.config, { + logger: env.logger, ++ // optional: alternatively, use scheduler with schedule defined in app-config.yaml + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), ++ // optional: alternatively, use schedule ++ scheduler: env.scheduler, + }), + ); @@ -66,19 +69,33 @@ catalog: filters: # optional projectKey: '^apis-.*$' # optional; RegExp repoSlug: '^service-.*$' # optional; RegExp + schedule: # optional; same options as in TaskScheduleDefinition + # supports cron, ISO duration, "human duration" as used in code + frequency: { minutes: 30 } + # supports ISO duration, "human duration" as used in code + timeout: { minutes: 3 } ``` -- **host**: +- **`host`**: The host of the Bitbucket Server instance, **note**: the host needs to registered as an integration as well, see [location](locations.md). - **`catalogPath`** _(optional)_: Default: `/catalog-info.yaml`. Path where to look for `catalog-info.yaml` files. When started with `/`, it is an absolute path from the repo root. -- **filters** _(optional)_: +- **`filters`** _(optional)_: - **`projectKey`** _(optional)_: Regular expression used to filter results based on the project key. - - **repoSlug** _(optional)_: + - **`repoSlug`** _(optional)_: Regular expression used to filter results based on the repo slug. +- **`schedule`** _(optional)_: + - **`frequency`**: + How often you want the task to run. The system does its best to avoid overlapping invocations. + - **`timeout`**: + The maximum amount of time that a single task invocation can take. + - **`initialDelay`** _(optional)_: + The amount of time that should pass before the first invocation happens. + - **`scope`** _(optional)_: + `'global'` or `'local'`. Sets the scope of concurrency control. ## Custom location processing @@ -93,18 +110,15 @@ repository. ```typescript const provider = BitbucketServerEntityProvider.fromConfig(env.config, { logger: env.logger, - schedule: env.scheduler.createScheduledTaskRunner({ - frequency: { minutes: 30 }, - timeout: { minutes: 3 }, - }), + schedule: env.scheduler, parser: async function* customLocationParser(options: { - location: LocationSpec, - client: BitbucketServerClient, + location: LocationSpec; + client: BitbucketServerClient; }) { // Custom logic for interpreting the matching repository // See defaultBitbucketServerLocationParser for an example - } -); + }, +}); ``` ## Alternative diff --git a/docs/integrations/gerrit/discovery.md b/docs/integrations/gerrit/discovery.md index 04fa3dac2a..7ed05d2b3b 100644 --- a/docs/integrations/gerrit/discovery.md +++ b/docs/integrations/gerrit/discovery.md @@ -32,10 +32,13 @@ const builder = await CatalogBuilder.create(env); builder.addEntityProvider( GerritEntityProvider.fromConfig(env.config, { logger: env.logger, + // optional: alternatively, use scheduler with schedule defined in app-config.yaml schedule: env.scheduler.createScheduledTaskRunner({ frequency: { minutes: 30 }, timeout: { minutes: 3 }, }), + // optional: alternatively, use schedule + scheduler: env.scheduler, }), ); ``` @@ -54,6 +57,11 @@ catalog: host: gerrit-your-company.com branch: master # Optional query: 'state=ACTIVE&prefix=webapps' + schedule: # optional; same options as in TaskScheduleDefinition + # supports cron, ISO duration, "human duration" as used in code + frequency: { minutes: 30 } + # supports ISO duration, "human duration" as used in code + timeout: { minutes: 3 } backend: host: gerrit-your-company.com branch: master # Optional @@ -62,8 +70,8 @@ catalog: The provider configuration is composed of three parts: -- host, the host of the Gerrit integration to use. -- branch, the branch where we will look for catalog entities (defaults to "master"). -- query, this string is directly used as the argument to the "List Project" API. - Typically you will want to have some filter here to exclude projects that will +- **`host`**: the host of the Gerrit integration to use. +- **`branch`** _(optional)_: the branch where we will look for catalog entities (defaults to "master"). +- **`query`**: this string is directly used as the argument to the "List Project" API. + Typically, you will want to have some filter here to exclude projects that will never contain any catalog files. diff --git a/docs/integrations/github/discovery.md b/docs/integrations/github/discovery.md index 742f40f8af..4f032901a9 100644 --- a/docs/integrations/github/discovery.md +++ b/docs/integrations/github/discovery.md @@ -30,19 +30,22 @@ And then add the entity provider to your catalog builder: ```diff // In packages/backend/src/plugins/catalog.ts -+ import { GitHubEntityProvider } from '@backstage/plugin-catalog-backend-module-github'; ++ import { GithubEntityProvider } from '@backstage/plugin-catalog-backend-module-github'; export default async function createPlugin( env: PluginEnvironment, ): Promise { const builder = await CatalogBuilder.create(env); + builder.addEntityProvider( -+ GitHubEntityProvider.fromConfig(env.config, { ++ GithubEntityProvider.fromConfig(env.config, { + logger: env.logger, ++ // optional: alternatively, use scheduler with schedule defined in app-config.yaml + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), ++ // optional: alternatively, use schedule ++ scheduler: env.scheduler, + }), + ); @@ -68,6 +71,11 @@ catalog: filters: branch: 'main' # string repository: '.*' # Regex + schedule: # optional; same options as in TaskScheduleDefinition + # supports cron, ISO duration, "human duration" as used in code + frequency: { minutes: 30 } + # supports ISO duration, "human duration" as used in code + timeout: { minutes: 3 } customProviderId: organization: 'new-org' # string catalogPath: '/custom/path/catalog-info.yaml' # string @@ -119,32 +127,41 @@ This provider supports multiple organizations via unique provider IDs. Path where to look for `catalog-info.yaml` files. You can use wildcards - `*` or `**` - to search the path and/or the filename. Wildcards cannot be used if the `validateLocationsExist` option is set to `true`. -- **filters** _(optional)_: - - **branch** _(optional)_: +- **`filters`** _(optional)_: + - **`branch`** _(optional)_: String used to filter results based on the branch name. - - **repository** _(optional)_: + - **`repository`** _(optional)_: Regular expression used to filter results based on the repository name. - - **topic** _(optional)_: + - **`topic`** _(optional)_: Both of the filters below may be used at the same time but the exclusion filter has the highest priority. In the example above, a repository with the `backstage-include` topic would still be excluded if it were also carrying the `experiments` topic. - - **include** _(optional)_: + - **`include`** _(optional)_: An array of strings used to filter in results based on their associated GitHub topics. If configured, only repositories with one (or more) topic(s) present in the inclusion filter will be ingested - - **exclude** _(optional)_: + - **`exclude`** _(optional)_: An array of strings used to filter out results based on their associated GitHub topics. If configured, all repositories _except_ those with one (or more) topics(s) present in the exclusion filter will be ingested. -- **organization**: +- **`host`** _(optional)_: + The hostname of your GitHub Enterprise instance. It must match a host defined in [integrations.github](locations.md). +- **`organization`**: Name of your organization account/workspace. If you want to add multiple organizations, you need to add one provider config each. -- **host** _(optional)_: - The hostname of your GitHub Enterprise instance. It must match a host defined in [integrations.github](locations.md). -- **validateLocationsExist** _(optional)_: +- **`validateLocationsExist`** _(optional)_: Whether to validate locations that exist before emitting them. This option avoids generating locations for catalog info files that do not exist in the source repository. Defaults to `false`. Due to limitations in the GitHub API's ability to query for repository objects, this option cannot be used in conjunction with wildcards in the `catalogPath`. +- **`schedule`** _(optional)_: + - **`frequency`**: + How often you want the task to run. The system does its best to avoid overlapping invocations. + - **`timeout`**: + The maximum amount of time that a single task invocation can take. + - **`initialDelay`** _(optional)_: + The amount of time that should pass before the first invocation happens. + - **`scope`** _(optional)_: + `'global'` or `'local'`. Sets the scope of concurrency control. ## GitHub API Rate Limits diff --git a/docs/integrations/gitlab/discovery.md b/docs/integrations/gitlab/discovery.md index df5aadcab4..dc0b911b3d 100644 --- a/docs/integrations/gitlab/discovery.md +++ b/docs/integrations/gitlab/discovery.md @@ -25,6 +25,11 @@ catalog: group: example-group # Optional. Group and subgroup (if needed) to look for repositories. If not present the whole project will be scanned entityFilename: catalog-info.yaml # Optional. Defaults to `catalog-info.yaml` projectPattern: /[\s\S]*/ # Optional. Filters found projects based on provided patter. Defaults to `/[\s\S]*/`, what means to not filter anything + schedule: # optional; same options as in TaskScheduleDefinition + # supports cron, ISO duration, "human duration" as used in code + frequency: { minutes: 30 } + # supports ISO duration, "human duration" as used in code + timeout: { minutes: 3 } ``` As this provider is not one of the default providers, you will first need to install @@ -47,10 +52,13 @@ const builder = await CatalogBuilder.create(env); builder.addEntityProvider( ...GitlabDiscoveryEntityProvider.fromConfig(env.config, { logger: env.logger, + // optional: alternatively, use scheduler with schedule defined in app-config.yaml schedule: env.scheduler.createScheduledTaskRunner({ frequency: { minutes: 30 }, timeout: { minutes: 3 }, }), + // optional: alternatively, use schedule + scheduler: env.scheduler, }), ); ``` diff --git a/docs/permissions/custom-rules.md b/docs/permissions/custom-rules.md index 1408d732d3..9407dc407a 100644 --- a/docs/permissions/custom-rules.md +++ b/docs/permissions/custom-rules.md @@ -14,12 +14,18 @@ Plugins should export a rule factory that provides type-safety that ensures comp import type { Entity } from '@backstage/plugin-catalog-model'; import { createCatalogPermissionRule } from '@backstage/plugin-catalog-backend/alpha'; import { createConditionFactory } from '@backstage/plugin-permission-node'; +import { z } from 'zod'; export const isInSystemRule = createCatalogPermissionRule({ name: 'IS_IN_SYSTEM', description: 'Checks if an entity is part of the system provided', resourceType: 'catalog-entity', - apply: (resource: Entity, systemRef: string) => { + paramsSchema: z.object({ + systemRef: z + .string() + .describe('SystemRef to check the resource is part of'), + }), + apply: (resource: Entity, { systemRef }) => { if (!resource.relations) { return false; } @@ -28,9 +34,9 @@ export const isInSystemRule = createCatalogPermissionRule({ .filter(relation => relation.type === 'partOf') .some(relation => relation.targetRef === systemRef); }, - toQuery: (systemRef: string) => ({ + toQuery: ({ systemRef }) => ({ key: 'relations.partOf', - value: systemRef, + values: [systemRef], }), }); @@ -85,14 +91,14 @@ class TestPermissionPolicy implements PermissionPolicy { if (isResourcePermission(request.permission, 'catalog-entity')) { return createCatalogConditionalDecision( request.permission, -- catalogConditions.isEntityOwner( -- user?.identity.ownershipEntityRefs ?? [], -- ), +- catalogConditions.isEntityOwner({ +- claims: user?.identity.ownershipEntityRefs ?? [], +- }), + { + anyOf: [ -+ catalogConditions.isEntityOwner( -+ user?.identity.ownershipEntityRefs ?? [] -+ ), ++ catalogConditions.isEntityOwner({ ++ claims: user?.identity.ownershipEntityRefs ?? [] ++ }), + isInSystem('interviewing') + ] + } diff --git a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md index 5bace9ad7b..8913b6d438 100644 --- a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md +++ b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md @@ -84,6 +84,7 @@ Create a new `plugins/todo-list-backend/src/service/rules.ts` file and append th ```typescript import { makeCreatePermissionRule } from '@backstage/plugin-permission-node'; import { TODO_LIST_RESOURCE_TYPE } from '@internal/plugin-todo-list-common'; +import { z } from 'zod'; import { Todo, TodoFilter } from './todos'; export const createTodoListPermissionRule = makeCreatePermissionRule< @@ -95,10 +96,13 @@ export const isOwner = createTodoListPermissionRule({ name: 'IS_OWNER', description: 'Should allow only if the todo belongs to the user', resourceType: TODO_LIST_RESOURCE_TYPE, - apply: (resource: Todo, userId: string) => { + paramsSchema: z.object({ + userId: z.string().describe('User ID to match on the resource') + }) + apply: (resource: Todo, { userId }) => { return resource.author === userId; }, - toQuery: (userId: string) => { + toQuery: ({ userId }) => { return { property: 'author', values: [userId], @@ -223,7 +227,9 @@ Let's go back to the permission policy's handle function and try to authorize ou + if (isPermission(request.permission, todoListUpdatePermission)) { + return createTodoListConditionalDecision( + request.permission, -+ todoListConditions.isOwner(user?.identity.userEntityRef), ++ todoListConditions.isOwner({ ++ userId: user?.identity.userEntityRef ?? '', ++ }), + ); + } + diff --git a/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md b/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md index c813ba594c..8ddcffdcd9 100644 --- a/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md +++ b/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md @@ -144,7 +144,9 @@ import { + ) { return createTodoListConditionalDecision( request.permission, - todoListConditions.isOwner(user?.identity.userEntityRef), + todoListConditions.isOwner({ + userId: user?.identity.userEntityRef + }), ); } ``` diff --git a/docs/permissions/writing-a-policy.md b/docs/permissions/writing-a-policy.md index c29424241c..7e741ae48f 100644 --- a/docs/permissions/writing-a-policy.md +++ b/docs/permissions/writing-a-policy.md @@ -70,9 +70,9 @@ Let's change the policy to the following: - }; + return createCatalogConditionalDecision( + request.permission, -+ catalogConditions.isEntityOwner( -+ user?.identity.ownershipEntityRefs ?? [], -+ ), ++ catalogConditions.isEntityOwner({ ++ claims: user?.identity.ownershipEntityRefs ?? [], ++ }), + ); } @@ -119,9 +119,9 @@ class TestPermissionPolicy implements PermissionPolicy { + if (isResourcePermission(request.permission, 'catalog-entity')) { return createCatalogConditionalDecision( request.permission, - catalogConditions.isEntityOwner( - user?.identity.ownershipEntityRefs ?? [], - ), + catalogConditions.isEntityOwner({ + claims: user?.identity.ownershipEntityRefs ?? [], + }), ); } diff --git a/docs/releases/v1.7.0-changelog.md b/docs/releases/v1.7.0-changelog.md new file mode 100644 index 0000000000..3653484952 --- /dev/null +++ b/docs/releases/v1.7.0-changelog.md @@ -0,0 +1,2589 @@ +# Release v1.7.0 + +## @backstage/cli@0.20.0 + +### Minor Changes + +- f368ad7279: **BREAKING**: Bumped `jest`, `jest-runtime`, and `jest-environment-jsdom` to v29. This is up from v27, so check out both the [v28](https://jestjs.io/docs/28.x/upgrading-to-jest28) and [v29](https://jestjs.io/docs/upgrading-to-jest29) (later [here](https://jestjs.io/docs/29.x/upgrading-to-jest29)) migration guides. + + Particular changes that where encountered in the main Backstage repo are: + + - The updated snapshot format. + - `jest.useFakeTimers('legacy')` is now `jest.useFakeTimers({ legacyFakeTimers: true })`. + - Error objects collected by `withLogCollector` from `@backstage/test-utils` are now objects with a `detail` property rather than a string. + +### Patch Changes + +- 78d5eb299e: Tweak the Jest Caching loader to only operate when in `watch` mode + +- 9c595302cb: Normalize on winston version ^3.2.1 + +- 24b40140c4: Treat files in `__testUtils__` and `__mocks__` directories as test files for linting + purposes. + + Updates the parts of the eslint configuration generator that specify which files + should be treated as test code to include any files under two additional + directories: + + - `__mocks__`: this is the directory used by Jest[0] for mock code. + - `__testUtils__`: a suggested location for utility code executed only when + running tests. + + [0]: https://jestjs.io/docs/manual-mocks#mocking-user-modules + +- 3e309107ca: Updated fallback versions of dependencies in all templates. + +- 292a088807: Added a new `repo test` command. + +- ba63cae41c: Updated lockfile parsing to have better support for Yarn 3. + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` + +- 2dddb32fea: Switched the Jest transform for YAML files to use a custom one available at `@backstage/cli/config/jestYamlTransform.js`. + +- a541a3a78a: Switch to upfront resolution of `swc-loader` in Webpack config. + +- cfb3598410: Removed `tsx` and `jsx` as supported extensions in backend packages. For most + repos, this will not have any effect. But if you inadvertently had added some + `tsx`/`jsx` files to your backend package, you may now start to see `code: 'MODULE_NOT_FOUND'` errors when launching the backend locally. The reason for + this is that the offending files get ignored during transpilation. Hence, the + importing file can no longer find anything to import. + + The fix is to rename any `.tsx` files in your backend packages to `.ts` instead, + or `.jsx` to `.js`. + +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3 + - @backstage/config-loader@1.1.5 + - @backstage/errors@1.1.2 + - @backstage/release-manifests@0.0.6 + - @backstage/types@1.0.0 + +## @backstage/plugin-auth-backend@0.17.0 + +### Minor Changes + +- e2dc42e9f0: Google OAuth refresh tokens will now be revoked on logout by calling Google's API +- 5fa831ce55: CookieConfigurer can optionally return the `SameSite` cookie attribute. + CookieConfigurer now requires an additional argument `appOrigin` - the origin URL of the app - which is used to calculate the `SameSite` attribute. + defaultCookieConfigurer returns the `SameSite` attribute which defaults to `Lax`. In cases where an auth-backend is running on a different domain than the App, `SameSite=None` is used - but only for secure contexts. This is so that cookies can be included in third-party requests. + + OAuthAdapterOptions has been modified to require additional arguments, `baseUrl`, and `cookieConfigurer`. + OAuthAdapter now resolves cookie configuration using its supplied CookieConfigurer for each request to make sure that the proper attributes always are set. + +### Patch Changes + +- b5c126010c: Auth0 provider now supports optional `connection` and `connectionScope` parameters to configure social identity providers. +- 8c6ec175bf: Fix GitLab provider setup so that it supports GitLab installations with a path in the URL. +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/catalog-client@1.1.1 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-bazaar-backend@0.2.0 + +### Minor Changes + +- 8554533546: **BREAKING** The bazaar-backend `createRouter` now requires that the `identityApi` is passed to the router. + + These changes are **required** to `packages/backend/src/plugins/bazaar.ts` + + The user entity ref is now added to the members table and is taken from the requesting user using the `identityApi`. + + ```diff + import { PluginEnvironment } from '../types'; + import { createRouter } from '@backstage/plugin-bazaar-backend'; + import { Router } from 'express'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + return await createRouter({ + logger: env.logger, + config: env.config, + database: env.database, + + identity: env.identity, + }); + } + ``` + +### Patch Changes + +- f7c2855d76: Router now also has endpoint `getLatestProjects` that takes a limit of projects as prop. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/backend-test-utils@0.1.29 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## @backstage/plugin-bitbucket-cloud-common@0.2.0 + +### Minor Changes + +- ad74723fbf: Update Bitbucket Cloud models to latest OAS version. + + The latest specification contained some BREAKING CHANGES + due to removed fields. + + All of these fields are not used at other plugins, though. + Therefore, this change has no impact on other modules here. + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.3.2 + +## @backstage/plugin-catalog@1.6.0 + +### Minor Changes + +- d558f41d3a: Added new column `Label` to `CatalogTable.columns`, this new column allows you make use of labels from metadata. + For example: category and visibility are type of labels associated with API entity illustrated below. + + YAML code snippet for API entity + + ```yaml + apiVersion: backstage.io/v1alpha1 + kind: API + metadata: + name: sample-api + description: API for sample + links: + - url: http://localhost:8080/swagger-ui.html + title: Swagger UI + tags: + - http + labels: + category: legacy + visibility: protected + ``` + + Consumers can customise columns to include label column and show in api-docs list + + ```typescript + const columns = [ + CatalogTable.columns.createNameColumn({ defaultKind: 'API' }), + CatalogTable.columns.createLabelColumn('category', { title: 'Category' }), + CatalogTable.columns.createLabelColumn('visibility', { + title: 'Visibility', + defaultValue: 'public', + }), + ]; + ``` + +### Patch Changes + +- 4efadb6968: Implemented the visual parts of `EntityKindPicker` so that it can be shown alongside the other filters on the left side of your catalog pages. +- 182000c663: Added `emptyContent` property to CatalogTable and DefaultCatalogPage to support customization of the Catalog Table. +- e89e1f614d: Added support for copy entity URL in entity page context menu +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/plugin-catalog-common@1.0.7 + - @backstage/catalog-client@1.1.1 + - @backstage/plugin-search-react@1.2.0 + - @backstage/plugin-search-common@1.1.0 + - @backstage/integration-react@1.1.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-backend@1.5.0 + +### Minor Changes + +- b2e6cb6acf: Added a new method `addLocationAnalyzers` to the `CatalogBuilder`. With this you can add location analyzers to your catalog. These analyzers will be used by the /analyze-location endpoint to decide if the provided URL contains any catalog-info.yaml files already or not. + + Moved the following types from this package to `@backstage/plugin-catalog-backend`. + + - AnalyzeLocationResponse + - AnalyzeLocationRequest + - AnalyzeLocationExistingEntity + - AnalyzeLocationGenerateEntity + - AnalyzeLocationEntityField + +- eb25f7e12d: The exported permission rules and the API of `createCatalogConditionalDecision` have changed to reflect the breaking changes made to the `PermissionRule` type. Note that all involved types are exported from `@backstage/plugin-catalog-backend/alpha` + +### Patch Changes + +- 8cb6e10105: Fixed a bug where entities provided without a location key would always replace existing entities, rather than updating them. +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- 63296ebcd4: Allow Placeholder value to be any value, not only string. +- 74022e0163: Make sure to stitch entities correctly after deletion, to ensure that their relations are updated. +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0 + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-common@1.0.7 + - @backstage/plugin-permission-node@0.7.0 + - @backstage/backend-plugin-api@0.1.3 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/catalog-client@1.1.1 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-scaffolder-common@1.2.1 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-import@0.9.0 + +### Minor Changes + +- b2e6cb6acf: **Breaking** + Moved the code search for the existing catalog-info.yaml files to the backend from the frontend. It means it will use the configured GitHub integration's credentials. + + Add the following to your `CatalogBuilder` to have the repo URL ingestion working again. + + ```ts + // catalog.ts + import { GithubLocationAnalyzer } from '@backstage/plugin-catalog-backend-module-github'; + ... + builder.addLocationAnalyzers( + new GithubLocationAnalyzer({ + discovery: env.discovery, + config: env.config, + }), + ); + ... + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/plugin-catalog-common@1.0.7 + - @backstage/catalog-client@1.1.1 + - @backstage/integration-react@1.1.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + +## @backstage/plugin-catalog-node@1.2.0 + +### Minor Changes + +- 404366c853: Deprecated the `LocationSpec` type. It got moved from this package to the `@backstage/plugin-catalog-common` so make sure imports are updated. + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-common@1.0.7 + - @backstage/backend-plugin-api@0.1.3 + - @backstage/catalog-client@1.1.1 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-react@1.2.0 + +### Minor Changes + +- 4efadb6968: Implemented the visual parts of `EntityKindPicker` so that it can be shown alongside the other filters on the left side of your catalog pages. + +### Patch Changes + +- 7939e743f5: Added two new `EntityRefLinks` props, the first being `getTitle` that allows for customization of the title used for each link. The second one is `fetchEntities`, which triggers a fetching of all entities so that the full entity definition is available in the `getTitle` callback. +- e9e532ebd8: Fixed issue where the query kind parameter is not honored +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/core-components@0.11.2 + - @backstage/plugin-catalog-common@1.0.7 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/catalog-client@1.1.1 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + - @backstage/version-bridge@1.0.1 + - @backstage/plugin-permission-react@0.4.6 + +## @backstage/plugin-permission-common@0.7.0 + +### Minor Changes + +- 46b4a72cee: **BREAKING**: When defining permission rules, it's now necessary to provide a [ZodSchema](https://github.com/colinhacks/zod) that specifies the parameters the rule expects. This has been added to help better describe the parameters in the response of the metadata endpoint and to validate the parameters before a rule is executed. + + To help with this, we have also made a change to the API of permission rules. Before, the permission rules `toQuery` and `apply` signature expected parameters to be separate arguments, like so... + + ```ts + createPermissionRule({ + apply: (resource, foo, bar) => true, + toQuery: (foo, bar) => {}, + }); + ``` + + The API has now changed to expect the parameters as a single object + + ```ts + createPermissionRule({ + paramSchema: z.object({ + foo: z.string().describe('Foo value to match'), + bar: z.string().describe('Bar value to match'), + }), + apply: (resource, { foo, bar }) => true, + toQuery: ({ foo, bar }) => {}, + }); + ``` + + One final change made is to limit the possible values for a parameter to primitives and arrays of primitives. + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-permission-node@0.7.0 + +### Minor Changes + +- 46b4a72cee: **BREAKING**: When defining permission rules, it's now necessary to provide a [ZodSchema](https://github.com/colinhacks/zod) that specifies the parameters the rule expects. This has been added to help better describe the parameters in the response of the metadata endpoint and to validate the parameters before a rule is executed. + + To help with this, we have also made a change to the API of permission rules. Before, the permission rules `toQuery` and `apply` signature expected parameters to be separate arguments, like so... + + ```ts + createPermissionRule({ + apply: (resource, foo, bar) => true, + toQuery: (foo, bar) => {}, + }); + ``` + + The API has now changed to expect the parameters as a single object + + ```ts + createPermissionRule({ + paramSchema: z.object({ + foo: z.string().describe('Foo value to match'), + bar: z.string().describe('Bar value to match'), + }), + apply: (resource, { foo, bar }) => true, + toQuery: ({ foo, bar }) => {}, + }); + ``` + + One final change made is to limit the possible values for a parameter to primitives and arrays of primitives. + +### Patch Changes + +- 9335ad115e: Exported types for the .metadata endpoint of the permission router +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## @backstage/plugin-playlist-backend@0.2.0 + +### Minor Changes + +- eb25f7e12d: **BREAKING** The exported permission rules have changed to reflect the breaking changes made to the PermissionRule type. + + For example, the `playlistConditions.isOwner` API has changed from: + + ```ts + playlistConditions.isOwner(['user:default/me', 'group:default/owner']); + ``` + + to: + + ```ts + playlistConditions.isOwner({ + owners: ['user:default/me', 'group:default/owner'], + }); + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/backend-test-utils@0.1.29 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/plugin-permission-node@0.7.0 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/catalog-client@1.1.1 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/plugin-playlist-common@0.1.1 + +## @backstage/plugin-scaffolder@1.7.0 + +### Minor Changes + +- f13d5f3f06: Add support for link to TechDocs and other links defined in template entity specification metadata on TemplateCard +- 05f22193c5: EntityPickers now support flags to control when to include default namespace + in result + +### Patch Changes + +- e4f0a96424: Making the description of the GitLab repoUrl owner field more clearer by focusing it refers to the GitLab namespace. +- 92e490d6b4: Make the `/next` scaffolder work end to end with the old `TaskPage` view +- 8220f2fd83: Support custom layouts in dry run editor +- 1047baa926: Bump to `react-jsonschema-form@v5-beta` for the `NextRouter` under `@alpha` exports +- 98ae18b68f: Fixed a bug where the `allowed*` values for the `RepoUrlPicker` would be reset on render. +- 8960d83013: Add support for `allowedOrganizations` and `allowedOwners` to the `AzureRepoPicker`. +- b681275e69: Ignore .git directories in Template Editor, increase upload limit for dry-runs to 10MB. +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/plugin-catalog-common@1.0.7 + - @backstage/catalog-client@1.1.1 + - @backstage/plugin-scaffolder-common@1.2.1 + - @backstage/integration-react@1.1.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + - @backstage/plugin-permission-react@0.4.6 + +## @backstage/plugin-scaffolder-backend@1.7.0 + +### Minor Changes + +- 253453fa14: Added a new property called `additionalTemplateGlobals` which allows you to add global functions to the scaffolder nunjucks templates. +- 17ff77154c: Update the `github:publish` action to allow passing whether pull + requests must be up to date with the default branch before merging. +- 304305dd20: Add `allowAutoMerge` option for `publish:github` action +- 694bfe2d61: Add functionality to shutdown scaffolder tasks if they are stale +- a8e9848479: Added optional `sourcePath` parameter to `publish:gitlab:merge-request` action, `targetPath` is now optional and falls back to current workspace path. + +### Patch Changes + +- 489621f613: Switching off duplicated timestamp in case of logging via task logger in a custom action +- 4880d43e25: Fixed setting default branch for Bitbucket Server +- b681275e69: Ignore .git directories in Template Editor, increase upload limit for dry-runs to 10MB. +- a35a27df70: Updated the `moduleId` of the experimental module export. +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0 + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/backend-tasks@0.3.6 + - @backstage/backend-plugin-api@0.1.3 + - @backstage/catalog-client@1.1.1 + - @backstage/plugin-scaffolder-common@1.2.1 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-search-backend@1.1.0 + +### Minor Changes + +- 16c853a6ed: Be less restrictive with unknown keys on query endpoint +- a799972bb1: The query received by search engines now contains a property called `pageLimit`, it specifies how many results to return per page when sending a query request to the search backend. + + Example: + _Returns up to 30 results per page_ + + GET /query?pageLimit=30 + + The search backend validates the page limit and this value must not exceed 100, but it doesn't set a default value for the page limit parameter, it leaves it up to each search engine to set this, so Lunr, Postgres and Elastic Search set 25 results per page as a default value. + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/plugin-permission-node@0.7.0 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-search-backend-node@1.0.3 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-search-common@1.1.0 + +### Minor Changes + +- a799972bb1: There is a new property called `pageLimit` on the `SearchQuery` interface that specifies how many results should be returned per page. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.7.0 + - @backstage/types@1.0.0 + +## @backstage/plugin-search-react@1.2.0 + +### Minor Changes + +- 4ed1fa2480: The search query state now has an optional `pageLimit` property that determines how many results will be requested per page, it defaults to 25. + + Examples: + _Basic_ + + ```jsx + + {results => { + // Item rendering logic is omitted + }} + + ``` + + _With context_ + + ```jsx + + + {results => { + // Item rendering logic is omitted + }} + + + ``` + +- bed5a1dc6e: The `` component now accepts an optional property `disableRenderingWithNoResults` to disable rendering when no results are returned. + Possibility to provide a custom no results component if needed through the `noResultsComponent` property. + + Examples: + + _Rendering a custom no results component_ + + ```jsx + } + /> + ``` + + _Disable rendering when there are no results_ + + ```jsx + + ``` + +- 3de4bd4f19: A `` component was created for limiting the number of results shown per search page. Use this new component to give users options to select how many search results they want to display per page. The default options are 10, 25, 50, 100. + + See examples below: + + _Basic_ + + ```jsx + import React, { useState } from 'react'; + import { Grid } from '@material-ui/core'; + import { Page, Header, Content, Lifecycle } from '@backstage/core-components'; + import { + SearchBarBase, + SearchPaginationBase, + SearchResultList, + } from '@backstage/plugin-search-react'; + + const SearchPage = () => { + const [term, setTerm] = useState(''); + const [pageLimit, setPageLimit] = useState(25); + const [pageCursor, setPageCursor] = useState(); + + return ( + +
} /> + + + + + + + + + + + + + + + ); + }; + ``` + + _With context_ + + ```jsx + import React from 'react'; + import { Grid } from '@material-ui/core'; + import { Page, Header, Content, Lifecycle } from '@backstage/core-components'; + import { + SearchBar, + SearchResult, + SearchPagination, + SearchResultListLayout, + SearchContextProvider, + DefaultResultListItem, + } from '@backstage/plugin-search-react'; + + const SearchPage = () => ( + + +
} /> + + + + + + + + + + + {({ results }) => ( + ( + + )} + /> + )} + + + + + + + ); + ``` + +- 6faaa05626: The `` component now accepts an optional property `disableRenderingWithNoResults` to disable rendering when no results are returned. + Possibility to provide a custom no results component if needed through the `noResultsComponent` property. + + Examples: + + _Rendering a custom no results component_ + + ```jsx + } + title="Documentation" + noResultsComponent={} + /> + ``` + + _Disable rendering when there are no results_ + + ```jsx + } + title="Documentation" + disableRenderingWithNoResults + /> + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/plugin-search-common@1.1.0 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + - @backstage/version-bridge@1.0.1 + +## @backstage/plugin-techdocs-backend@1.4.0 + +### Minor Changes + +- 7ced1b4076: Add optional `catalogClient` argument to `createRoute` parameters + +### Patch Changes + +- 8006f8a602: In order to improve the debuggability of the search indexing process, messages logged during indexing are now tagged with a `documentType` whose value corresponds to the `type` being indexed. +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-common@1.0.7 + - @backstage/plugin-techdocs-node@1.4.1 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/catalog-client@1.1.1 + - @backstage/plugin-search-common@1.1.0 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + +## @backstage/plugin-user-settings@0.5.0 + +### Minor Changes + +- 5543e86660: **BREAKING**: The `apiRef` passed to `ProviderSettingsItem` now needs to + implement `ProfileInfoApi & SessionApi`, rather than just the latter. This is + unlikely to have an effect on most users though, since the builtin auth + providers generally implement both. + + Fixed settings page showing providers as logged out when the user is using more + than one provider, and displayed some additional login information. + +### Patch Changes + +- 06d61d1266: Handle errors that may occur when the user logs out +- 44c9a95dcf: Prevent `.set()` to execute a request to the StorageClient if the user is `guest` +- 174f02a00a: Update installation instructions +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-app-api@1.1.1 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + +## @backstage/app-defaults@1.0.7 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-app-api@1.1.1 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + - @backstage/plugin-permission-react@0.4.6 + +## @backstage/backend-app-api@0.2.2 + +### Patch Changes + +- 0027a749cd: Added possibility to configure index plugin of the HTTP router service. +- 45857bffae: Properly export `rootLoggerFactory`. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/backend-tasks@0.3.6 + - @backstage/plugin-permission-node@0.7.0 + - @backstage/backend-plugin-api@0.1.3 + - @backstage/errors@1.1.2 + +## @backstage/backend-common@0.15.2 + +### Patch Changes + +- e8d7976413: Added back support for when no branch is provided for the Bitbucket Server `UrlReader` +- c44cf412de: Fix BitBucket server integration +- c31f7cdfbc: Fixed an issue where `getClient()` for a `pluginId` would return different clients and not share them +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3 + - @backstage/config-loader@1.1.5 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## @backstage/backend-defaults@0.1.2 + +### Patch Changes + +- 96d288a02d: Added root logger service to the set of default services. +- Updated dependencies + - @backstage/backend-app-api@0.2.2 + - @backstage/backend-plugin-api@0.1.3 + +## @backstage/backend-plugin-api@0.1.3 + +### Patch Changes + +- 28377dc89f: Allow interfaces to be used for inferred option types. +- a35a27df70: Added documentation for `createBackendModule`, with guidelines for choosing a module ID. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/backend-tasks@0.3.6 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/config@1.0.3 + +## @backstage/backend-tasks@0.3.6 + +### Patch Changes + +- d4fea86ea3: Added new function `readTaskScheduleDefinitionFromConfig` to read `TaskScheduleDefinition` (aka. schedule) from the `Config`. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## @backstage/backend-test-utils@0.1.29 + +### Patch Changes + +- 72549952d1: Fixed handling of root scoped services in `startTestBackend`. +- e91e8e9c55: Increased test database max connection pool size to reduce the risk of resource exhaustion. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/cli@0.20.0 + - @backstage/backend-app-api@0.2.2 + - @backstage/backend-plugin-api@0.1.3 + - @backstage/config@1.0.3 + +## @backstage/catalog-client@1.1.1 + +### Patch Changes + +- 4f2ac624b4: Renamed argument in `validateEntity` from `location` to `locationRef` +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/errors@1.1.2 + +## @backstage/catalog-model@1.1.2 + +### Patch Changes + +- 6f3b8d0962: Defer `ajv` compilation of schema validators to improve module-import performance +- Updated dependencies + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## @backstage/codemods@0.1.40 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.10 + +## @backstage/config@1.0.3 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + +## @backstage/config-loader@1.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## @backstage/core-app-api@1.1.1 + +### Patch Changes + +- 27e6404aba: Fixed a bug where gathered index routes would fail to bind routable extensions. This would typically show up when placing a routable extension in the entity page overview tab. +- Updated dependencies + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/types@1.0.0 + - @backstage/version-bridge@1.0.1 + +## @backstage/core-components@0.11.2 + +### Patch Changes + +- bde1e8c8e2: Added `curve` prop to the `DependencyGraph` component to select the type of layout +- 882101cd9b: Deep-import LightAsync component to improve module-import speed +- 0d0f30d87e: Fixed layout for core-components docs where table was broken by splitting with code sample +- Updated dependencies + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + - @backstage/version-bridge@1.0.1 + +## @backstage/core-plugin-api@1.0.7 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3 + - @backstage/types@1.0.0 + - @backstage/version-bridge@1.0.1 + +## @backstage/create-app@0.4.32 + +### Patch Changes + +- 58c2264325: Newly created Backstage repositories now use the stable version 6 of + `react-router`, just like the main repo does. Please let us know if you find any + issues with this. + + Migrating to the stable version of `react-router` is optional for the time + being. But if you want to do the same for your existing repository, please + follow [this + guide](https://backstage.io/docs/tutorials/react-router-stable-migration). + +- e05e0f021b: Update versions of packages used in the create-app template, to match those in the main repo + +- 01dff06be4: Leverage cache mounts in Dockerfile during `yarn install ...` and `apt-get ...` commands to speed up repeated builds. + +- 90616bcaa6: Add the new search pagination component to the search page template. + +- 7c6306fc8a: Initializes a git repository when creating an app using @packages/create-app + +- 52f25858a8: Added `*.session.sql` Visual Studio Code database functionality files to `.gitignore` in the default template. This is optional but potentially helpful if your developers use Visual Studio Code; you can add a line with that exact value to your own root `.gitignore` if you want the same. + +- 6d00e80146: Updated the root `test` scripts to use `backstage-cli repo test`. + + To apply this change to an existing app, make the following change to the root `package.json`: + + ```diff + - "test": "backstage-cli test", + - "test:all": "lerna run test -- --coverage", + + "test": "backstage-cli repo test", + + "test:all": "backstage-cli repo test --coverage", + ``` + +- Updated dependencies + - @backstage/cli-common@0.1.10 + +## @backstage/dev-utils@1.0.7 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-app-api@1.1.1 + - @backstage/test-utils@1.2.1 + - @backstage/app-defaults@1.0.7 + - @backstage/integration-react@1.1.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/errors@1.1.2 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + +## @backstage/integration@1.3.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## @backstage/integration-react@1.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/integration@1.3.2 + - @backstage/theme@0.2.16 + +## @techdocs/cli@1.2.2 + +### Patch Changes + +- 0b2a30dead: fixing techdocs-cli Docker client creation + + Docker client does not need to be created when --no-docker + option is provided. + + If you had DOCKER_CERT_PATH environment variable defined + the Docker client was looking for certificates + and breaking techdocs-cli generate command even with --no-docker + option. + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-techdocs-node@1.4.1 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3 + +## @backstage/test-utils@1.2.1 + +### Patch Changes + +- e05e0f021b: Align on the version of `@material-ui/icons` used, to `^4.9.1` like other packages in the main repo +- Updated dependencies + - @backstage/core-app-api@1.1.1 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + - @backstage/plugin-permission-react@0.4.6 + +## @backstage/plugin-adr@0.2.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/plugin-search-react@1.2.0 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-adr-common@0.2.2 + - @backstage/integration-react@1.1.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-adr-backend@0.2.2 + +### Patch Changes + +- 8006f8a602: In order to improve the debuggability of the search indexing process, messages logged during indexing are now tagged with a `documentType` whose value corresponds to the `type` being indexed. +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/catalog-client@1.1.1 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-adr-common@0.2.2 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + +## @backstage/plugin-adr-common@0.2.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-search-common@1.1.0 + - @backstage/integration@1.3.2 + +## @backstage/plugin-airbrake@0.3.10 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/test-utils@1.2.1 + - @backstage/dev-utils@1.0.7 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-airbrake-backend@0.2.10 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/config@1.0.3 + +## @backstage/plugin-allure@0.1.26 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-analytics-module-ga@0.1.21 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/theme@0.2.16 + +## @backstage/plugin-apache-airflow@0.2.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + +## @backstage/plugin-api-docs@0.8.10 + +### Patch Changes + +- 50c6e14aee: Updated dependency `@asyncapi/react-component` to `1.0.0-next.43`. +- 3d5bb521ee: Updated dependency `@asyncapi/react-component` to `1.0.0-next.42`. +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/plugin-catalog@1.6.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-apollo-explorer@0.1.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-app-backend@0.3.37 + +### Patch Changes + +- 11c9e0ad33: Added alpha plugin implementation for the new backend system. Available at `@backstage/plugin-app-backend/alpha`. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/backend-plugin-api@0.1.3 + - @backstage/config@1.0.3 + - @backstage/config-loader@1.1.5 + - @backstage/types@1.0.0 + +## @backstage/plugin-auth-node@0.2.6 + +### Patch Changes + +- f3a3fefb96: Ensure `getIdentity` throws an `AuthenticationError` instead of a `NotAllowed` error when authentication fails +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## @backstage/plugin-azure-devops@0.2.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-azure-devops-common@0.3.0 + +## @backstage/plugin-azure-devops-backend@0.3.16 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/config@1.0.3 + - @backstage/plugin-azure-devops-common@0.3.0 + +## @backstage/plugin-badges@0.2.34 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-badges-backend@0.1.31 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/catalog-client@1.1.1 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## @backstage/plugin-bazaar@0.1.25 + +### Patch Changes + +- f7c2855d76: Added a `Overview Card` for either latest or random projects. Changed `ProjectPreview.tsx` so it take `gridSize` and `useTablePagination` as props. +- c0352bbc69: Link to the user catalog entity of a member +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/plugin-catalog@1.6.0 + - @backstage/core-components@0.11.2 + - @backstage/cli@0.20.0 + - @backstage/catalog-client@1.1.1 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + +## @backstage/plugin-bitrise@0.1.37 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-catalog-backend-module-aws@0.1.10 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-backend-module-azure@0.1.8 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-backend-module-bitbucket@0.2.4 + +### Patch Changes + +- 23f9199a0f: Deprecate `@backstage/plugin-catalog-backend-module-bitbucket`. + + Please migrate to `@backstage/plugin-catalog-backend-module-bitbucket-cloud` + or `@backstage/plugin-catalog-backend-module-bitbucket-server` instead. + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.0 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.1.4 + +### Patch Changes + +- f66e696e7b: Bitbucket Cloud provider: Add option to configure schedule via `app-config.yaml` instead of in code. + + Please find how to configure the schedule at the config at + + +- a9b91d39bb: Add `bitbucketCloudCatalogModule` (new backend-plugin-api, alpha). + +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/backend-plugin-api@0.1.3 + - @backstage/plugin-bitbucket-cloud-common@0.2.0 + - @backstage/config@1.0.3 + - @backstage/integration@1.3.2 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.1.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + +## @backstage/plugin-catalog-backend-module-github@0.1.8 + +### Patch Changes + +- 8749df3d02: `GitHubEntityProvider`: Add option to configure schedule via `app-config.yaml` instead of in code. + + Please find how to configure the schedule at the config at + + +- 7022aebf35: Added `GithubLocationAnalyzer`. This can be used to add to the `CatalogBuilder`. When added this will be used by `RepoLocationAnalyzer` to figure out if the given URL that you are trying to import from the /catalog-import page already contains catalog-info.yaml files. + +- 51046b58b0: Use schedule from config at backend module. + + Also, it removes `GithubEntityProviderCatalogModuleOptions` + in favor of config-only for the backend module setup + like at other similar modules. + +- 7edb5909e8: Add missing config schema for the `GitHubEntityProvider`. + +- be9474b103: Replaces in-code uses of `GitHub` by `Github` and deprecates old versions. + + Deprecates + + - `GitHubEntityProvider` replaced by `GithubEntityProvider` + - `GitHubLocationAnalyzer` replaced by `GithubLocationAnalyzer` + - `GitHubLocationAnalyzerOptions` replaced by `GithubLocationAnalyzerOptions` + - `GitHubOrgEntityProvider` replaced by `GithubOrgEntityProvider` + - `GitHubOrgEntityProviderOptions` replaced by `GithubOrgEntityProviderOptions` + + Renames + + - `GitHubLocationAnalyzer` to `GithubLocationAnalyzer` + - `GitHubLocationAnalyzerOptions` to `GithubLocationAnalyzerOptions` + +- a35a27df70: Updated the `moduleId` of the experimental module export. + +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0 + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/backend-plugin-api@0.1.3 + - @backstage/catalog-client@1.1.1 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-backend-module-gitlab@0.1.8 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-backend-module-ldap@0.5.4 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-backend-module-msgraph@0.4.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + +## @backstage/plugin-catalog-backend-module-openapi@0.1.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0 + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/config@1.0.3 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-common@1.0.7 + +### Patch Changes + +- 823acaa88b: Moved the following types from `@backstage/plugin-catalog-backend` to this package. + + - AnalyzeLocationResponse + - AnalyzeLocationRequest + - AnalyzeLocationExistingEntity + - AnalyzeLocationGenerateEntity + - AnalyzeLocationEntityField + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/plugin-search-common@1.1.0 + +## @backstage/plugin-catalog-graph@0.2.22 + +### Patch Changes + +- bde1e8c8e2: Added `curve` prop to the `DependencyGraph` component to select the type of layout +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/catalog-client@1.1.1 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-catalog-graphql@0.3.14 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/config@1.0.3 + - @backstage/types@1.0.0 + +## @backstage/plugin-cicd-statistics@0.1.12 + +### Patch Changes + +- e05e0f021b: Align on the version of `@material-ui/icons` used, to `^4.9.1` like other packages in the main repo +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-plugin-api@1.0.7 + +## @backstage/plugin-cicd-statistics-module-gitlab@0.1.6 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-cicd-statistics@0.1.12 + - @backstage/core-plugin-api@1.0.7 + +## @backstage/plugin-circleci@0.3.10 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-cloudbuild@0.3.10 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-code-climate@0.1.10 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-code-coverage@0.2.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-code-coverage-backend@0.2.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/catalog-client@1.1.1 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + +## @backstage/plugin-codescene@0.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-config-schema@0.1.33 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + +## @backstage/plugin-cost-insights@0.11.32 + +### Patch Changes + +- a94c2ed1b7: Fixed bug in `CostOverviewBreakdownChart` component where some datasets caused the cost overview breakdown chart to tear. +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/theme@0.2.16 + - @backstage/plugin-cost-insights-common@0.1.1 + +## @backstage/plugin-dynatrace@1.0.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-explore@0.3.41 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + - @backstage/plugin-explore-react@0.0.22 + +## @backstage/plugin-explore-react@0.0.22 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.0.7 + +## @backstage/plugin-firehydrant@0.1.27 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-fossa@0.2.42 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-gcalendar@0.3.6 + +### Patch Changes + +- 4c2ed7ecf1: Fixed loader showing when user not signed in +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-gcp-projects@0.3.29 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-git-release-manager@0.3.23 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/integration@1.3.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-github-actions@0.5.10 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/integration@1.3.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-github-deployments@0.1.41 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/integration-react@1.1.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-github-issues@0.1.2 + +### Patch Changes + +- 8c7bff2bb4: Updated the `luxon` dependency to 3.x +- 719ccbb963: Properly filter on relations instead of the spec, when finding by owner +- df226e124c: Add filtering and ordering to the graphql query +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-github-pull-requests-board@0.1.4 + +### Patch Changes + +- 719ccbb963: Properly filter on relations instead of the spec, when finding by owner +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/integration@1.3.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-gitops-profiles@0.3.28 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-gocd@0.1.16 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-graphiql@0.2.42 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-graphql-backend@0.1.27 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-graphql@0.3.14 + - @backstage/config@1.0.3 + +## @backstage/plugin-home@0.4.26 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/plugin-stack-overflow@0.1.6 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/theme@0.2.16 + +## @backstage/plugin-ilert@0.1.36 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-jenkins@0.7.9 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-jenkins-common@0.1.9 + +## @backstage/plugin-jenkins-backend@0.1.27 + +### Patch Changes + +- b19ea927af: Fixed a bug where `extraRequestHeaders` configuration was ignored. +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/catalog-client@1.1.1 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/plugin-jenkins-common@0.1.9 + +## @backstage/plugin-jenkins-common@0.1.9 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.7 + - @backstage/plugin-permission-common@0.7.0 + +## @backstage/plugin-kafka@0.3.10 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/theme@0.2.16 + +## @backstage/plugin-kafka-backend@0.2.30 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## @backstage/plugin-kubernetes@0.7.3 + +### Patch Changes + +- 51af8361de: Add useCustomResources react hook for fetching Kubernetes Custom Resources +- 35a6cfe257: Fix infinite call bug in `useCustomResources` hook +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/plugin-kubernetes-common@0.4.3 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/theme@0.2.16 + +## @backstage/plugin-kubernetes-backend@0.7.3 + +### Patch Changes + +- de676888bc: Added missing cluster locator configuration schema entries, for the catalog and local proxy types +- d4a8c683be: kubernetes service locator now take request context parameters +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/catalog-client@1.1.1 + - @backstage/plugin-kubernetes-common@0.4.3 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## @backstage/plugin-kubernetes-common@0.4.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + +## @backstage/plugin-lighthouse@0.3.10 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/theme@0.2.16 + +## @backstage/plugin-newrelic@0.3.28 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-newrelic-dashboard@0.2.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + +## @backstage/plugin-org@0.5.10 + +### Patch Changes + +- f2b4b55636: consistently show parent and child relations in group profile card +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-pagerduty@0.5.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-periskop@0.1.8 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-periskop-backend@0.1.8 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/config@1.0.3 + +## @backstage/plugin-permission-backend@0.5.12 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/plugin-permission-node@0.7.0 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## @backstage/plugin-permission-react@0.4.6 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.7.0 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + +## @backstage/plugin-playlist@0.1.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/plugin-catalog-common@1.0.7 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/plugin-search-react@1.2.0 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-permission-react@0.4.6 + - @backstage/plugin-playlist-common@0.1.1 + +## @backstage/plugin-playlist-common@0.1.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.7.0 + +## @backstage/plugin-proxy-backend@0.2.31 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/config@1.0.3 + +## @backstage/plugin-rollbar@0.4.10 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-rollbar-backend@0.1.34 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/config@1.0.3 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.12 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/plugin-scaffolder-backend@1.7.0 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-scaffolder-backend-module-rails@0.4.5 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/plugin-scaffolder-backend@1.7.0 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.2.10 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.7.0 + - @backstage/config@1.0.3 + - @backstage/types@1.0.0 + +## @backstage/plugin-scaffolder-common@1.2.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-search@1.0.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/plugin-search-react@1.2.0 + - @backstage/plugin-search-common@1.1.0 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + - @backstage/version-bridge@1.0.1 + +## @backstage/plugin-search-backend-module-elasticsearch@1.0.3 + +### Patch Changes + +- a799972bb1: The search engine has been updated to take advantage of the `pageLimit` property on search queries. If none is provided, the search engine will continue to use its default value of 25 results per page. +- 8006f8a602: In order to improve the debuggability of the search indexing process, messages logged during indexing are now tagged with a `documentType` whose value corresponds to the `type` being indexed. +- Updated dependencies + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-search-backend-node@1.0.3 + - @backstage/config@1.0.3 + +## @backstage/plugin-search-backend-module-pg@0.4.1 + +### Patch Changes + +- a799972bb1: The search engine has been updated to take advantage of the `pageLimit` property on search queries. If none is provided, the search engine will continue to use its default value of 25 results per page. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-search-backend-node@1.0.3 + - @backstage/config@1.0.3 + +## @backstage/plugin-search-backend-node@1.0.3 + +### Patch Changes + +- a799972bb1: The search engine has been updated to take advantage of the `pageLimit` property on search queries. If none is provided, the search engine will continue to use its default value of 25 results per page. +- 8006f8a602: In order to improve the debuggability of the search indexing process, messages logged during indexing are now tagged with a `documentType` whose value corresponds to the `type` being indexed. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/backend-tasks@0.3.6 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/plugin-search-common@1.1.0 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## @backstage/plugin-sentry@0.4.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-shortcuts@0.3.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + +## @backstage/plugin-sonarqube@0.4.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-sonarqube-backend@0.1.2 + +### Patch Changes + +- 9c595302cb: Normalize on winston version ^3.2.1 +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## @backstage/plugin-splunk-on-call@0.3.34 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-stack-overflow@0.1.6 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-home@0.4.26 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/theme@0.2.16 + +## @backstage/plugin-stack-overflow-backend@0.1.6 + +### Patch Changes + +- 8006f8a602: In order to improve the debuggability of the search indexing process, messages logged during indexing are now tagged with a `documentType` whose value corresponds to the `type` being indexed. +- Updated dependencies + - @backstage/cli@0.20.0 + - @backstage/plugin-search-common@1.1.0 + - @backstage/config@1.0.3 + +## @backstage/plugin-tech-insights@0.3.1 + +### Patch Changes + +- f3d272cf57: Make sure to reload score card contents when props change +- a60a6807bd: making available the search for the last FACTS executed +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + - @backstage/plugin-tech-insights-common@0.2.7 + +## @backstage/plugin-tech-insights-backend@0.5.3 + +### Patch Changes + +- 296aea34da: The Tech Insights plugin supports running fact retrievers across multiple instances. Update the README to remove the stale instructions. +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- f7cbfb97ed: Modify router endpoint to handle singular and collections of request parameters similarly. +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/backend-tasks@0.3.6 + - @backstage/catalog-client@1.1.1 + - @backstage/plugin-tech-insights-node@0.3.5 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/plugin-tech-insights-common@0.2.7 + +## @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.21 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/plugin-tech-insights-node@0.3.5 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/plugin-tech-insights-common@0.2.7 + +## @backstage/plugin-tech-insights-common@0.2.7 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + +## @backstage/plugin-tech-insights-node@0.3.5 + +### Patch Changes + +- 0963b4d5fb: Updated package role to be `node-library`. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + - @backstage/types@1.0.0 + - @backstage/plugin-tech-insights-common@0.2.7 + +## @backstage/plugin-tech-radar@0.5.17 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-techdocs@1.3.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/plugin-search-react@1.2.0 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-techdocs-react@1.0.5 + - @backstage/integration-react@1.1.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.5 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.6.0 + - @backstage/core-components@0.11.2 + - @backstage/core-app-api@1.1.1 + - @backstage/test-utils@1.2.1 + - @backstage/plugin-search-react@1.2.0 + - @backstage/plugin-techdocs@1.3.3 + - @backstage/plugin-techdocs-react@1.0.5 + - @backstage/integration-react@1.1.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @backstage/plugin-techdocs-module-addons-contrib@1.0.5 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/plugin-techdocs-react@1.0.5 + - @backstage/integration-react@1.1.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/integration@1.3.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-techdocs-node@1.4.1 + +### Patch Changes + +- 0b2a30dead: fixing techdocs-cli Docker client creation + + Docker client does not need to be created when --no-docker + option is provided. + + If you had DOCKER_CERT_PATH environment variable defined + the Docker client was looking for certificates + and breaking techdocs-cli generate command even with --no-docker + option. + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-search-common@1.1.0 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + +## @backstage/plugin-techdocs-react@1.0.5 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/version-bridge@1.0.1 + +## @backstage/plugin-todo@0.2.12 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-todo-backend@0.1.34 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/catalog-client@1.1.1 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + +## @backstage/plugin-user-settings-backend@0.1.1 + +### Patch Changes + +- f3463b176b: Use `Response.status` instead of `.send(number)` +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- 82ac9bcfe5: Fix wrong import statement in `README.md`. +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-vault@0.1.4 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-vault-backend@0.2.3 + +### Patch Changes + +- 9c595302cb: Normalize on winston version ^3.2.1 +- dae0bbe522: VaultBuilder.tsx renamed to VaultBuilder in order for module to be correctly loaded. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/backend-test-utils@0.1.29 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## @backstage/plugin-xcmetrics@0.2.30 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + +## example-app@0.2.76 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-user-settings@0.5.0 + - @backstage/plugin-scaffolder@1.7.0 + - @backstage/plugin-org@0.5.10 + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/plugin-catalog-graph@0.2.22 + - @backstage/core-app-api@1.1.1 + - @backstage/cli@0.20.0 + - @backstage/plugin-catalog-common@1.0.7 + - @backstage/plugin-catalog-import@0.9.0 + - @backstage/plugin-tech-insights@0.3.1 + - @backstage/plugin-kubernetes@0.7.3 + - @backstage/plugin-gcalendar@0.3.6 + - @backstage/plugin-api-docs@0.8.10 + - @backstage/plugin-search-react@1.2.0 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-cost-insights@0.11.32 + - @backstage/plugin-airbrake@0.3.10 + - @backstage/plugin-azure-devops@0.2.1 + - @backstage/plugin-badges@0.2.34 + - @backstage/plugin-circleci@0.3.10 + - @backstage/plugin-cloudbuild@0.3.10 + - @backstage/plugin-code-coverage@0.2.3 + - @backstage/plugin-dynatrace@1.0.0 + - @backstage/plugin-explore@0.3.41 + - @backstage/plugin-github-actions@0.5.10 + - @backstage/plugin-gocd@0.1.16 + - @backstage/plugin-home@0.4.26 + - @backstage/plugin-jenkins@0.7.9 + - @backstage/plugin-kafka@0.3.10 + - @backstage/plugin-lighthouse@0.3.10 + - @backstage/plugin-newrelic-dashboard@0.2.3 + - @backstage/plugin-pagerduty@0.5.3 + - @backstage/plugin-playlist@0.1.1 + - @backstage/plugin-rollbar@0.4.10 + - @backstage/plugin-search@1.0.3 + - @backstage/plugin-sentry@0.4.3 + - @backstage/plugin-techdocs@1.3.3 + - @backstage/plugin-techdocs-react@1.0.5 + - @backstage/plugin-todo@0.2.12 + - @internal/plugin-catalog-customized@0.0.3 + - @backstage/app-defaults@1.0.7 + - @backstage/integration-react@1.1.5 + - @backstage/plugin-apache-airflow@0.2.3 + - @backstage/plugin-gcp-projects@0.3.29 + - @backstage/plugin-graphiql@0.2.42 + - @backstage/plugin-newrelic@0.3.28 + - @backstage/plugin-shortcuts@0.3.2 + - @backstage/plugin-stack-overflow@0.1.6 + - @backstage/plugin-tech-radar@0.5.17 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/theme@0.2.16 + - @backstage/plugin-permission-react@0.4.6 + +## example-backend@0.2.76 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/plugin-scaffolder-backend@1.7.0 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/backend-tasks@0.3.6 + - @backstage/plugin-permission-node@0.7.0 + - @backstage/plugin-auth-backend@0.17.0 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/plugin-tech-insights-backend@0.5.3 + - @backstage/plugin-search-backend@1.1.0 + - @backstage/catalog-client@1.1.1 + - @backstage/plugin-playlist-backend@0.2.0 + - @backstage/plugin-jenkins-backend@0.1.27 + - @backstage/plugin-app-backend@0.3.37 + - @backstage/plugin-badges-backend@0.1.31 + - @backstage/plugin-graphql-backend@0.1.27 + - @backstage/plugin-permission-backend@0.5.12 + - @backstage/plugin-rollbar-backend@0.1.34 + - @backstage/plugin-kubernetes-backend@0.7.3 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-search-backend-node@1.0.3 + - @backstage/plugin-search-backend-module-pg@0.4.1 + - @backstage/plugin-search-backend-module-elasticsearch@1.0.3 + - @backstage/plugin-techdocs-backend@1.4.0 + - @backstage/plugin-tech-insights-node@0.3.5 + - example-app@0.2.76 + - @backstage/plugin-code-coverage-backend@0.2.3 + - @backstage/plugin-kafka-backend@0.2.30 + - @backstage/plugin-todo-backend@0.1.34 + - @backstage/plugin-azure-devops-backend@0.3.16 + - @backstage/plugin-proxy-backend@0.2.31 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.5 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.21 + - @backstage/config@1.0.3 + - @backstage/integration@1.3.2 + +## example-backend-next@0.0.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/plugin-scaffolder-backend@1.7.0 + - @backstage/backend-defaults@0.1.2 + - @backstage/plugin-app-backend@0.3.37 + +## techdocs-cli-embedded-app@0.2.75 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog@1.6.0 + - @backstage/core-components@0.11.2 + - @backstage/core-app-api@1.1.1 + - @backstage/cli@0.20.0 + - @backstage/test-utils@1.2.1 + - @backstage/plugin-techdocs@1.3.3 + - @backstage/plugin-techdocs-react@1.0.5 + - @backstage/app-defaults@1.0.7 + - @backstage/integration-react@1.1.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/theme@0.2.16 + +## @internal/plugin-catalog-customized@0.0.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/plugin-catalog@1.6.0 + +## @internal/plugin-todo-list@1.0.6 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## @internal/plugin-todo-list-backend@1.0.6 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## @internal/plugin-todo-list-common@1.0.5 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.7.0 diff --git a/docs/releases/v1.7.0-next.2-changelog.md b/docs/releases/v1.7.0-next.2-changelog.md new file mode 100644 index 0000000000..d5a9b0ddad --- /dev/null +++ b/docs/releases/v1.7.0-next.2-changelog.md @@ -0,0 +1,2127 @@ +# Release v1.7.0-next.2 + +## @backstage/plugin-bazaar-backend@0.2.0-next.2 + +### Minor Changes + +- 8554533546: **BREAKING** The bazaar-backend `createRouter` now requires that the `identityApi` is passed to the router. + + These changes are **required** to `packages/backend/src/plugins/bazaar.ts` + + The user entity ref is now added to the members table and is taken from the requesting user using the `identityApi`. + + ```diff + import { PluginEnvironment } from '../types'; + import { createRouter } from '@backstage/plugin-bazaar-backend'; + import { Router } from 'express'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + return await createRouter({ + logger: env.logger, + config: env.config, + database: env.database, + + identity: env.identity, + }); + } + ``` + +### Patch Changes + +- f7c2855d76: Router now also has endpoint `getLatestProjects` that takes a limit of projects as prop. +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/backend-test-utils@0.1.29-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/plugin-catalog-backend@1.5.0-next.2 + +### Minor Changes + +- b2e6cb6acf: Added a new method `addLocationAnalyzers` to the `CatalogBuilder`. With this you can add location analyzers to your catalog. These analyzers will be used by the /analyze-location endpoint to decide if the provided URL contains any catalog-info.yaml files already or not. + + Moved the following types from this package to `@backstage/plugin-catalog-backend`. + + - AnalyzeLocationResponse + - AnalyzeLocationRequest + - AnalyzeLocationExistingEntity + - AnalyzeLocationGenerateEntity + - AnalyzeLocationEntityField + +- eb25f7e12d: The exported permission rules and the API of `createCatalogConditionalDecision` have changed to reflect the breaking changes made to the `PermissionRule` type. Note that all involved types are exported from `@backstage/plugin-catalog-backend/alpha` + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0-next.2 + - @backstage/plugin-catalog-common@1.0.7-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-permission-node@0.7.0-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + - @backstage/plugin-scaffolder-common@1.2.1-next.2 + +## @backstage/plugin-catalog-import@0.9.0-next.2 + +### Minor Changes + +- b2e6cb6acf: **Breaking** + Moved the code search for the existing catalog-info.yaml files to the backend from the frontend. It means it will use the configured GitHub integration's credentials. + + Add the following to your `CatalogBuilder` to have the repo URL ingestion working again. + + ```ts + // catalog.ts + import { GitHubLocationAnalyzer } from '@backstage/plugin-catalog-backend-module-github'; + ... + builder.addLocationAnalyzers( + new GitHubLocationAnalyzer({ + discovery: env.discovery, + config: env.config, + }), + ); + ... + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.7-next.2 + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/integration-react@1.1.5-next.2 + +## @backstage/plugin-catalog-node@1.2.0-next.2 + +### Minor Changes + +- 404366c853: Deprecated the `LocationSpec` type. It got moved from this package to the `@backstage/plugin-catalog-common` so make sure imports are updated. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.7-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-permission-common@0.7.0-next.2 + +### Minor Changes + +- 46b4a72cee: **BREAKING**: When defining permission rules, it's now necessary to provide a [ZodSchema](https://github.com/colinhacks/zod) that specifies the parameters the rule expects. This has been added to help better describe the parameters in the response of the metadata endpoint and to validate the parameters before a rule is executed. + + To help with this, we have also made a change to the API of permission rules. Before, the permission rules `toQuery` and `apply` signature expected parameters to be separate arguments, like so... + + ```ts + createPermissionRule({ + apply: (resource, foo, bar) => true, + toQuery: (foo, bar) => {}, + }); + ``` + + The API has now changed to expect the parameters as a single object + + ```ts + createPermissionRule({ + paramSchema: z.object({ + foo: z.string().describe('Foo value to match'), + bar: z.string().describe('Bar value to match'), + }), + apply: (resource, { foo, bar }) => true, + toQuery: ({ foo, bar }) => {}, + }); + ``` + + One final change made is to limit the possible values for a parameter to primitives and arrays of primitives. + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-permission-node@0.7.0-next.2 + +### Minor Changes + +- 46b4a72cee: **BREAKING**: When defining permission rules, it's now necessary to provide a [ZodSchema](https://github.com/colinhacks/zod) that specifies the parameters the rule expects. This has been added to help better describe the parameters in the response of the metadata endpoint and to validate the parameters before a rule is executed. + + To help with this, we have also made a change to the API of permission rules. Before, the permission rules `toQuery` and `apply` signature expected parameters to be separate arguments, like so... + + ```ts + createPermissionRule({ + apply: (resource, foo, bar) => true, + toQuery: (foo, bar) => {}, + }); + ``` + + The API has now changed to expect the parameters as a single object + + ```ts + createPermissionRule({ + paramSchema: z.object({ + foo: z.string().describe('Foo value to match'), + bar: z.string().describe('Bar value to match'), + }), + apply: (resource, { foo, bar }) => true, + toQuery: ({ foo, bar }) => {}, + }); + ``` + + One final change made is to limit the possible values for a parameter to primitives and arrays of primitives. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/plugin-playlist-backend@0.2.0-next.2 + +### Minor Changes + +- eb25f7e12d: **BREAKING** The exported permission rules have changed to reflect the breaking changes made to the PermissionRule type. + + For example, the `playlistConditions.isOwner` API has changed from: + + ```ts + playlistConditions.isOwner(['user:default/me', 'group:default/owner']); + ``` + + to: + + ```ts + playlistConditions.isOwner({ + owners: ['user:default/me', 'group:default/owner'], + }); + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-permission-node@0.7.0-next.2 + - @backstage/backend-test-utils@0.1.29-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/plugin-playlist-common@0.1.1-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/plugin-scaffolder-backend@1.7.0-next.2 + +### Minor Changes + +- 17ff77154c: Update the `github:publish` action to allow passing whether pull + requests must be up to date with the default branch before merging. +- a8e9848479: Added optional `sourcePath` parameter to `publish:gitlab:merge-request` action, `targetPath` is now optional and falls back to current workspace path. + +### Patch Changes + +- 4880d43e25: Fixed setting default branch for Bitbucket Server +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0-next.2 + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + - @backstage/plugin-scaffolder-common@1.2.1-next.2 + +## @backstage/plugin-techdocs-backend@1.4.0-next.2 + +### Minor Changes + +- 7ced1b4076: Add optional `catalogClient` argument to `createRoute` parameters + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.7-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-techdocs-node@1.4.1-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + +## @backstage/app-defaults@1.0.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-react@0.4.6-next.2 + - @backstage/core-app-api@1.1.1-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/backend-app-api@0.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-node@0.7.0-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/backend-common@0.15.2-next.2 + +### Patch Changes + +- c44cf412de: Fix BitBucket server integration +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3-next.2 + - @backstage/config-loader@1.1.5-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/backend-defaults@0.1.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.2.2-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + +## @backstage/backend-plugin-api@0.1.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/config@1.0.3-next.2 + +## @backstage/backend-tasks@0.3.6-next.2 + +### Patch Changes + +- d4fea86ea3: Added new function `readTaskScheduleDefinitionFromConfig` to read `TaskScheduleDefinition` (aka. schedule) from the `Config`. +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/backend-test-utils@0.1.29-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/cli@0.20.0-next.2 + - @backstage/backend-app-api@0.2.2-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + - @backstage/config@1.0.3-next.2 + +## @backstage/catalog-client@1.1.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/catalog-model@1.1.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/cli@0.20.0-next.2 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3-next.2 + - @backstage/config-loader@1.1.5-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/release-manifests@0.0.6 + - @backstage/types@1.0.0 + +## @backstage/codemods@0.1.40-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.10 + +## @backstage/config@1.0.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + +## @backstage/config-loader@1.1.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/core-app-api@1.1.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/types@1.0.0 + - @backstage/version-bridge@1.0.1 + +## @backstage/core-components@0.11.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + - @backstage/version-bridge@1.0.1 + +## @backstage/core-plugin-api@1.0.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/types@1.0.0 + - @backstage/version-bridge@1.0.1 + +## @backstage/create-app@0.4.32-next.2 + +### Patch Changes + +- 01dff06be4: Leverage cache mounts in Dockerfile during `yarn install ...` and `apt-get ...` commands to speed up repeated builds. +- Updated dependencies + - @backstage/cli-common@0.1.10 + +## @backstage/dev-utils@1.0.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/test-utils@1.2.1-next.2 + - @backstage/app-defaults@1.0.7-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-app-api@1.1.1-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + +## @backstage/errors@1.1.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + +## @backstage/integration@1.3.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/integration-react@1.1.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/theme@0.2.16 + +## @techdocs/cli@1.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-techdocs-node@1.4.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3-next.2 + +## @backstage/test-utils@1.2.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-permission-react@0.4.6-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-app-api@1.1.1-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + +## @backstage/plugin-adr@0.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-adr-common@0.2.2-next.2 + - @backstage/plugin-search-react@1.2.0-next.2 + +## @backstage/plugin-adr-backend@0.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/plugin-adr-common@0.2.2-next.2 + +## @backstage/plugin-adr-common@0.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + +## @backstage/plugin-airbrake@0.3.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/test-utils@1.2.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/dev-utils@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-airbrake-backend@0.2.10-next.2 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/config@1.0.3-next.2 + +## @backstage/plugin-allure@0.1.26-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-analytics-module-ga@0.1.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-apache-airflow@0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + +## @backstage/plugin-api-docs@0.8.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.6.0-next.2 + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-apollo-explorer@0.1.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-app-backend@0.3.37-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/config-loader@1.1.5-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-auth-backend@0.17.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-auth-node@0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/plugin-azure-devops@0.2.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-azure-devops-common@0.3.0 + +## @backstage/plugin-azure-devops-backend@0.3.16-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/plugin-azure-devops-common@0.3.0 + +## @backstage/plugin-badges@0.2.34-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-badges-backend@0.1.31-next.2 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/plugin-bazaar@0.1.25-next.2 + +### Patch Changes + +- f7c2855d76: Added a `Overview Card` for either latest or random projects. Changed `ProjectPreview.tsx` so it take `gridSize` and `useTablePagination` as props. +- c0352bbc69: Link to the user catalog entity of a member +- Updated dependencies + - @backstage/cli@0.20.0-next.2 + - @backstage/plugin-catalog@1.6.0-next.2 + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/plugin-bitbucket-cloud-common@0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.3.2-next.2 + +## @backstage/plugin-bitrise@0.1.37-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-catalog@1.6.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.7-next.2 + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + - @backstage/plugin-search-react@1.2.0-next.2 + +## @backstage/plugin-catalog-backend-module-aws@0.1.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-backend-module-azure@0.1.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-backend-module-bitbucket@0.2.4-next.2 + +### Patch Changes + +- 23f9199a0f: Deprecate `@backstage/plugin-catalog-backend-module-bitbucket`. + + Please migrate to `@backstage/plugin-catalog-backend-module-bitbucket-cloud` + or `@backstage/plugin-catalog-backend-module-bitbucket-server` instead. + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.0-next.2 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.1.4-next.2 + +### Patch Changes + +- f66e696e7b: Bitbucket Cloud provider: Add option to configure schedule via `app-config.yaml` instead of in code. + + Please find how to configure the schedule at the config at + + +- a9b91d39bb: Add `bitbucketCloudCatalogModule` (new backend-plugin-api, alpha). + +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0-next.2 + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/plugin-bitbucket-cloud-common@0.2.0-next.2 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.1.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + +## @backstage/plugin-catalog-backend-module-github@0.1.8-next.2 + +### Patch Changes + +- 7022aebf35: Added `GitHubLocationAnalyzer`. This can be used to add to the `CatalogBuilder`. When added this will be used by `RepoLocationAnalyzer` to figure out if the given URL that you are trying to import from the /catalog-import page already contains catalog-info.yaml files. +- 7edb5909e8: Add missing config schema for the `GitHubEntityProvider`. +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0-next.2 + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-backend-module-gitlab@0.1.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-backend-module-ldap@0.5.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-backend-module-msgraph@0.4.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + +## @backstage/plugin-catalog-backend-module-openapi@0.1.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0-next.2 + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-common@1.0.7-next.2 + +### Patch Changes + +- 823acaa88b: Moved the following types from `@backstage/plugin-catalog-backend` to this package. + + - AnalyzeLocationResponse + - AnalyzeLocationRequest + - AnalyzeLocationExistingEntity + - AnalyzeLocationGenerateEntity + - AnalyzeLocationEntityField + +- Updated dependencies + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + +## @backstage/plugin-catalog-graph@0.2.22-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-catalog-graphql@0.3.14-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-catalog-react@1.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.7-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-permission-react@0.4.6-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + - @backstage/version-bridge@1.0.1 + +## @backstage/plugin-cicd-statistics@0.1.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + +## @backstage/plugin-cicd-statistics-module-gitlab@0.1.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/plugin-cicd-statistics@0.1.12-next.2 + +## @backstage/plugin-circleci@0.3.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-cloudbuild@0.3.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-code-climate@0.1.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-code-coverage@0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-code-coverage-backend@0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + +## @backstage/plugin-codescene@0.1.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-config-schema@0.1.33-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + +## @backstage/plugin-cost-insights@0.11.32-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-cost-insights-common@0.1.1 + +## @backstage/plugin-dynatrace@1.0.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-explore@0.3.41-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-explore-react@0.0.22-next.2 + +## @backstage/plugin-explore-react@0.0.22-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.0.7-next.2 + +## @backstage/plugin-firehydrant@0.1.27-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-fossa@0.2.42-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-gcalendar@0.3.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-gcp-projects@0.3.29-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-git-release-manager@0.3.23-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-github-actions@0.5.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-github-deployments@0.1.41-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-github-issues@0.1.2-next.2 + +### Patch Changes + +- df226e124c: Add filtering and ordering to the graphql query +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-github-pull-requests-board@0.1.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-gitops-profiles@0.3.28-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-gocd@0.1.16-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-graphiql@0.2.42-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-graphql-backend@0.1.27-next.2 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/plugin-catalog-graphql@0.3.14-next.2 + +## @backstage/plugin-home@0.4.26-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-stack-overflow@0.1.6-next.2 + +## @backstage/plugin-ilert@0.1.36-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-jenkins@0.7.9-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/plugin-jenkins-common@0.1.9-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-jenkins-backend@0.1.27-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-jenkins-common@0.1.9-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/plugin-jenkins-common@0.1.9-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.7-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + +## @backstage/plugin-kafka@0.3.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-kafka-backend@0.2.30-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/plugin-kubernetes@0.7.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-kubernetes-common@0.4.3-next.2 + +## @backstage/plugin-kubernetes-backend@0.7.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/plugin-kubernetes-common@0.4.3-next.2 + +## @backstage/plugin-kubernetes-common@0.4.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2-next.2 + +## @backstage/plugin-lighthouse@0.3.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-newrelic@0.3.28-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-newrelic-dashboard@0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/plugin-org@0.5.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-pagerduty@0.5.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-periskop@0.1.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-periskop-backend@0.1.8-next.2 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/config@1.0.3-next.2 + +## @backstage/plugin-permission-backend@0.5.12-next.2 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-permission-node@0.7.0-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/plugin-permission-react@0.4.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + +## @backstage/plugin-playlist@0.1.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.7-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/plugin-permission-react@0.4.6-next.2 + - @backstage/plugin-playlist-common@0.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-search-react@1.2.0-next.2 + +## @backstage/plugin-playlist-common@0.1.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.7.0-next.2 + +## @backstage/plugin-proxy-backend@0.2.31-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/config@1.0.3-next.2 + +## @backstage/plugin-rollbar@0.4.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-rollbar-backend@0.1.34-next.2 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/config@1.0.3-next.2 + +## @backstage/plugin-scaffolder@1.7.0-next.2 + +### Patch Changes + +- 92e490d6b4: Make the `/next` scaffolder work end to end with the old `TaskPage` view +- 1047baa926: Bump to `react-jsonschema-form@v5-beta` for the `NextRouter` under `@alpha` exports +- 98ae18b68f: Fixed a bug where the `allowed*` values for the `RepoUrlPicker` would be reset on render. +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.7-next.2 + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/plugin-permission-react@0.4.6-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + - @backstage/plugin-scaffolder-common@1.2.1-next.2 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-scaffolder-backend@1.7.0-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-scaffolder-backend-module-rails@0.4.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-scaffolder-backend@1.7.0-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.2.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.7.0-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-scaffolder-common@1.2.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-search@1.0.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + - @backstage/version-bridge@1.0.1 + - @backstage/plugin-search-react@1.2.0-next.2 + +## @backstage/plugin-search-backend@1.1.0-next.2 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-permission-node@0.7.0-next.2 + - @backstage/plugin-search-backend-node@1.0.3-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-search-backend-module-elasticsearch@1.0.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.0.3-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/config@1.0.3-next.2 + +## @backstage/plugin-search-backend-module-pg@0.4.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-search-backend-node@1.0.3-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/config@1.0.3-next.2 + +## @backstage/plugin-search-backend-node@1.0.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/plugin-search-common@1.1.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-search-react@1.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + - @backstage/version-bridge@1.0.1 + +## @backstage/plugin-sentry@0.4.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-shortcuts@0.3.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + +## @backstage/plugin-sonarqube@0.4.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-sonarqube-backend@0.1.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/plugin-splunk-on-call@0.3.34-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-stack-overflow@0.1.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-home@0.4.26-next.2 + +## @backstage/plugin-stack-overflow-backend@0.1.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.20.0-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/config@1.0.3-next.2 + +## @backstage/plugin-tech-insights@0.3.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + - @backstage/plugin-tech-insights-common@0.2.7-next.2 + +## @backstage/plugin-tech-insights-backend@0.5.3-next.2 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-tech-insights-node@0.3.5-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/plugin-tech-insights-common@0.2.7-next.2 + +## @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-tech-insights-node@0.3.5-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/plugin-tech-insights-common@0.2.7-next.2 + +## @backstage/plugin-tech-insights-common@0.2.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + +## @backstage/plugin-tech-insights-node@0.3.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/types@1.0.0 + - @backstage/plugin-tech-insights-common@0.2.7-next.2 + +## @backstage/plugin-tech-radar@0.5.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-techdocs@1.3.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-search-react@1.2.0-next.2 + - @backstage/plugin-techdocs-react@1.0.5-next.2 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.6.0-next.2 + - @backstage/test-utils@1.2.1-next.2 + - @backstage/core-app-api@1.1.1-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-search-react@1.2.0-next.2 + - @backstage/plugin-techdocs@1.3.3-next.2 + - @backstage/plugin-techdocs-react@1.0.5-next.2 + +## @backstage/plugin-techdocs-module-addons-contrib@1.0.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-techdocs-react@1.0.5-next.2 + +## @backstage/plugin-techdocs-node@1.4.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + +## @backstage/plugin-techdocs-react@1.0.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/version-bridge@1.0.1 + +## @backstage/plugin-todo@0.2.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-todo-backend@0.1.34-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + +## @backstage/plugin-user-settings@0.5.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.1.1-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + +## @backstage/plugin-user-settings-backend@0.1.1-next.2 + +### Patch Changes + +- f3463b176b: Use `Response.status` instead of `.send(number)` +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + +## @backstage/plugin-vault@0.1.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + +## @backstage/plugin-vault-backend@0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/backend-test-utils@0.1.29-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + +## @backstage/plugin-xcmetrics@0.2.30-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + +## example-app@0.2.76-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder@1.7.0-next.2 + - @backstage/plugin-catalog-common@1.0.7-next.2 + - @backstage/plugin-catalog-import@0.9.0-next.2 + - @backstage/cli@0.20.0-next.2 + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/plugin-playlist@0.1.1-next.2 + - @backstage/plugin-permission-react@0.4.6-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/app-defaults@1.0.7-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-app-api@1.1.1-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-airbrake@0.3.10-next.2 + - @backstage/plugin-apache-airflow@0.2.3-next.2 + - @backstage/plugin-api-docs@0.8.10-next.2 + - @backstage/plugin-azure-devops@0.2.1-next.2 + - @backstage/plugin-badges@0.2.34-next.2 + - @backstage/plugin-catalog-graph@0.2.22-next.2 + - @backstage/plugin-circleci@0.3.10-next.2 + - @backstage/plugin-cloudbuild@0.3.10-next.2 + - @backstage/plugin-code-coverage@0.2.3-next.2 + - @backstage/plugin-cost-insights@0.11.32-next.2 + - @backstage/plugin-dynatrace@1.0.0-next.2 + - @backstage/plugin-explore@0.3.41-next.2 + - @backstage/plugin-gcalendar@0.3.6-next.2 + - @backstage/plugin-gcp-projects@0.3.29-next.2 + - @backstage/plugin-github-actions@0.5.10-next.2 + - @backstage/plugin-gocd@0.1.16-next.2 + - @backstage/plugin-graphiql@0.2.42-next.2 + - @backstage/plugin-home@0.4.26-next.2 + - @backstage/plugin-jenkins@0.7.9-next.2 + - @backstage/plugin-kafka@0.3.10-next.2 + - @backstage/plugin-kubernetes@0.7.3-next.2 + - @backstage/plugin-lighthouse@0.3.10-next.2 + - @backstage/plugin-newrelic@0.3.28-next.2 + - @backstage/plugin-newrelic-dashboard@0.2.3-next.2 + - @backstage/plugin-org@0.5.10-next.2 + - @backstage/plugin-pagerduty@0.5.3-next.2 + - @backstage/plugin-rollbar@0.4.10-next.2 + - @backstage/plugin-search@1.0.3-next.2 + - @backstage/plugin-search-react@1.2.0-next.2 + - @backstage/plugin-sentry@0.4.3-next.2 + - @backstage/plugin-shortcuts@0.3.2-next.2 + - @backstage/plugin-stack-overflow@0.1.6-next.2 + - @backstage/plugin-tech-insights@0.3.1-next.2 + - @backstage/plugin-tech-radar@0.5.17-next.2 + - @backstage/plugin-techdocs@1.3.3-next.2 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.5-next.2 + - @backstage/plugin-techdocs-react@1.0.5-next.2 + - @backstage/plugin-todo@0.2.12-next.2 + - @backstage/plugin-user-settings@0.5.0-next.2 + - @internal/plugin-catalog-customized@0.0.3-next.2 + +## example-backend@0.2.76-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-permission-node@0.7.0-next.2 + - @backstage/plugin-scaffolder-backend@1.7.0-next.2 + - @backstage/plugin-playlist-backend@0.2.0-next.2 + - @backstage/plugin-badges-backend@0.1.31-next.2 + - @backstage/plugin-graphql-backend@0.1.27-next.2 + - @backstage/plugin-permission-backend@0.5.12-next.2 + - @backstage/plugin-rollbar-backend@0.1.34-next.2 + - @backstage/plugin-search-backend@1.1.0-next.2 + - @backstage/plugin-tech-insights-backend@0.5.3-next.2 + - @backstage/plugin-techdocs-backend@1.4.0-next.2 + - example-app@0.2.76-next.2 + - @backstage/plugin-search-backend-node@1.0.3-next.2 + - @backstage/plugin-tech-insights-node@0.3.5-next.2 + - @backstage/plugin-app-backend@0.3.37-next.2 + - @backstage/plugin-auth-backend@0.17.0-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/plugin-azure-devops-backend@0.3.16-next.2 + - @backstage/plugin-code-coverage-backend@0.2.3-next.2 + - @backstage/plugin-jenkins-backend@0.1.27-next.2 + - @backstage/plugin-kafka-backend@0.2.30-next.2 + - @backstage/plugin-kubernetes-backend@0.7.3-next.2 + - @backstage/plugin-proxy-backend@0.2.31-next.2 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.5-next.2 + - @backstage/plugin-search-backend-module-elasticsearch@1.0.3-next.2 + - @backstage/plugin-search-backend-module-pg@0.4.1-next.2 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.21-next.2 + - @backstage/plugin-todo-backend@0.1.34-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/integration@1.3.2-next.2 + +## example-backend-next@0.0.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/plugin-scaffolder-backend@1.7.0-next.2 + - @backstage/plugin-app-backend@0.3.37-next.2 + - @backstage/backend-defaults@0.1.2-next.2 + +## techdocs-cli-embedded-app@0.2.75-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.20.0-next.2 + - @backstage/plugin-catalog@1.6.0-next.2 + - @backstage/test-utils@1.2.1-next.2 + - @backstage/app-defaults@1.0.7-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-app-api@1.1.1-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-techdocs@1.3.3-next.2 + - @backstage/plugin-techdocs-react@1.0.5-next.2 + +## @internal/plugin-catalog-customized@0.0.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.6.0-next.2 + - @backstage/plugin-catalog-react@1.2.0-next.2 + +## @internal/plugin-todo-list@1.0.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + +## @internal/plugin-todo-list-backend@1.0.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + +## @internal/plugin-todo-list-common@1.0.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.7.0-next.2 diff --git a/docs/releases/v1.7.0.md b/docs/releases/v1.7.0.md new file mode 100644 index 0000000000..cb87ead4c9 --- /dev/null +++ b/docs/releases/v1.7.0.md @@ -0,0 +1,125 @@ +--- +id: v1.7.0 +title: v1.7.0 +description: Backstage Release v1.7.0 +--- + +These are the release notes for the v1.7.0 release of +[Backstage](https://backstage.io/). + +A huge thanks to the whole team of maintainers and contributors as well as the +amazing Backstage Community for the hard work in getting this release developed +and done. + +## Highlights + +### GitHub Catalog Import now Powered by the Backend + +The analysis performed during catalog imports (i.e. when supplying the URL of a +repository rather than an individual YAML file in the Create flow) is now +powered by the backend rather than frontend code. This means that the catalog +backend needs to be supplied with a location analyzer for this use case to +continue to function. + +If you want to make use of this feature, check out the installation instructions +in [the +changelog](https://github.com/backstage/backstage/blob/master/plugins/catalog-import/CHANGELOG.md#090). + +Contributed by [@kissmikijr](https://github.com/kissmikijr) in +[#13800](https://github.com/backstage/backstage/pull/13800) + +### Permission Rule Changes + +When defining permission rules, it's now necessary to provide a [Zod +Schema](https://github.com/colinhacks/zod) that specifies the parameters the +rule expects. This has been added to help better describe the parameters in the +response of the metadata endpoint and to validate the parameters before a rule +is executed. The signatures of the rule methods (`apply` and `toQuery`) have +changed slightly as well. + +You can read more about this in [the permissions +documentation](https://backstage.io/docs/permissions/overview) and [the +changelog](https://github.com/backstage/backstage/blob/master/plugins/permission-node/CHANGELOG.md#070). + +### Migration: `jest` v29 + +Both `jest`, `jest-runtime`, and `jest-environment-jsdom` as used by the +Backstage CLI were bumped to version 29. This is up from version 27, so check +out both the [v28](https://jestjs.io/docs/28.x/upgrading-to-jest28) and +[v29](https://jestjs.io/docs/upgrading-to-jest29) (later +[here](https://jestjs.io/docs/29.x/upgrading-to-jest29)) migration guides, since +your existing tests may be affected. + +Particular changes that were encountered in the main Backstage repository are: + +- The updated snapshot format. +- `jest.useFakeTimers('legacy')` is now `jest.useFakeTimers({ legacyFakeTimers: true })`. +- Error objects collected by `withLogCollector` from `@backstage/test-utils` are + now objects with a `detail` property rather than a string. + +### Migration: `react-router` v6 + +Newly created Backstage repositories now use the stable version 6 of +`react-router`, just like the main repository does. + +Migrating to the stable version of `react-router` is optional for the time +being; Backstage has support for both versions. But if you want to do the same +for your existing repository, please follow [this +guide](https://backstage.io/docs/tutorials/react-router-stable-migration). +Support for the beta version will be removed in a later release. + +### Support for `__mocks__` and `__testUtils__` directories + +The Backstage CLI now has built-in support for `__mocks__` and `__testUtils__` +directories in your code. These can be used for mocks and shared utilities in +tests. + +### New Arguments for the Router of `@backstage/plugin-bazaar-backend` + +The bazaar-backend `createRouter` function now requires that the `identityApi` +is passed to the router. + +### Deprecated plugin: `@backstage/plugin-catalog-backend-module-bitbucket` + +This has been deprecated and split into +`@backstage/plugin-catalog-backend-module-bitbucket-cloud` and +`@backstage/plugin-catalog-backend-module-bitbucket-server`, for BitBucket Cloud +and BitBucket Server respectively. Please update your dependencies accordingly, +depending on which product you use. + +The original package will be removed in a future release. + +Contributed by [@pjungermann](https://github.com/pjungermann) in +[#14070](https://github.com/backstage/backstage/pull/14070) + +## Security Fixes + +This release does not contain any security fixes. + +## Upgrade path + +We recommend that you keep your Backstage project up to date with this latest +release. For more guidance on how to upgrade, check out the documentation for +[keeping Backstage +updated](https://backstage.io/docs/getting-started/keeping-backstage-updated). + +## Links and References + +Below you can find a list of links and references to help you learn about and +start using this new release. + +- [Backstage official website](https://backstage.io/), + [documentation](https://backstage.io/docs/), and [getting started + guide](https://backstage.io/docs/getting-started/) +- [GitHub repository](https://github.com/backstage/backstage) +- Backstage's [versioning and support + policy](https://backstage.io/docs/overview/versioning-policy) +- [Community Discord](https://discord.gg/bFESRKVt) for discussions and support +- [Changelog](https://github.com/backstage/backstage/tree/master/docs/releases/v1.7.0-changelog.md) +- Backstage [Demos](https://backstage.io/demos), + [Blog](https://backstage.io/blog), + [Roadmap](https://backstage.io/docs/overview/roadmap) and + [Plugins](https://backstage.io/plugins) + +Sign up for our [newsletter](https://mailchi.mp/spotify/backstage-community) if +you want to be informed about what is happening in the world of Backstage. diff --git a/docs/tutorials/react-router-stable-migration.md b/docs/tutorials/react-router-stable-migration.md index 9636345116..bb01157bdb 100644 --- a/docs/tutorials/react-router-stable-migration.md +++ b/docs/tutorials/react-router-stable-migration.md @@ -178,7 +178,7 @@ When migrating over to React Router v6 stable, you might also see browser consol ```diff - -+ } /> ++ } /> ``` ### `NavLink` diff --git a/docs/tutorials/switching-sqlite-postgres.md b/docs/tutorials/switching-sqlite-postgres.md index c0c35999c6..bd067ee388 100644 --- a/docs/tutorials/switching-sqlite-postgres.md +++ b/docs/tutorials/switching-sqlite-postgres.md @@ -72,6 +72,12 @@ backend: + port: ${POSTGRES_PORT} + user: ${POSTGRES_USER} + password: ${POSTGRES_PASSWORD} ++ # https://node-postgres.com/features/ssl ++ # you can set the sslmode configuration option via the `PGSSLMODE` environment variable ++ # see https://www.postgresql.org/docs/current/libpq-ssl.html Table 33.1. SSL Mode Descriptions (e.g. require) ++ # ssl: ++ # ca: # if you have a CA file and want to verify it you can uncomment this section ++ # $file: /ca/server.crt + # Refer to Tarn docs for default values on PostgreSQL pool configuration - https://github.com/Vincit/tarn.js + knexConfig: + pool: @@ -79,12 +85,6 @@ backend: + max: 12 + acquireTimeoutMillis: 60000 + idleTimeoutMillis: 60000 -+ # https://node-postgres.com/features/ssl -+ # you can set the sslmode configuration option via the `PGSSLMODE` environment variable -+ # see https://www.postgresql.org/docs/current/libpq-ssl.html Table 33.1. SSL Mode Descriptions (e.g. require) -+ # ssl: -+ # ca: # if you have a CA file and want to verify it you can uncomment this section -+ # $file: /ca/server.crt ``` ### Using a single database diff --git a/microsite/data/plugins/azure-sites.yaml b/microsite/data/plugins/azure-sites.yaml new file mode 100644 index 0000000000..3ea9d0439b --- /dev/null +++ b/microsite/data/plugins/azure-sites.yaml @@ -0,0 +1,8 @@ +--- +title: Azure Sites +author: FRISS +authorUrl: https://friss.com +category: Infrastructure +description: Azure Sites (Apps & Functions) support for a given entity. View the current status of the site, quickly jump to site's Overview page, or Log Stream page. +documentation: https://github.com/backstage/backstage/tree/master/plugins/azure-sites +npmPackageName: '@backstage/plugin-azure-sites' diff --git a/microsite/data/plugins/gke-usage.yaml b/microsite/data/plugins/gke-usage.yaml deleted file mode 100644 index 9668b18fdf..0000000000 --- a/microsite/data/plugins/gke-usage.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: GKE Usage -author: BESTSELLER -authorUrl: https://bestsellerit.com -category: Discovery -description: This plugin will show you the cost and resource usage of your application within Google Kubernetes Engine (GKE). -documentation: https://github.com/BESTSELLER/backstage-plugin-gkeusage/blob/master/README.md -iconUrl: img/gke-logo.png -npmPackageName: '@bestsellerit/backstage-plugin-gkeusage' -tags: - - gke - - cost - - google - - usage - - metering diff --git a/microsite/pages/en/plugins.js b/microsite/pages/en/plugins.js index de336444ab..91f4138174 100644 --- a/microsite/pages/en/plugins.js +++ b/microsite/pages/en/plugins.js @@ -16,11 +16,30 @@ const { const pluginsDirectory = require('path').join(process.cwd(), 'data/plugins'); const pluginMetadata = fs .readdirSync(pluginsDirectory) - .map(file => yaml.load(fs.readFileSync(`./data/plugins/${file}`, 'utf8'))) + .map(file => { + const fileContent = fs.readFileSync(`./data/plugins/${file}`, 'utf8'); + let metadata = yaml.load(fileContent); + + const gitIsoDate = require('child_process') + .execFileSync('git', [ + 'log', + '-1', + '--format="%ai"', + '--reverse', + `./data/plugins/${file}`, + ]) + .toString(); + + metadata.date = new Date(gitIsoDate); + + return metadata; + }) .sort((a, b) => a.title.toLowerCase().localeCompare(b.title.toLowerCase())); const truncate = text => text.length > 170 ? text.substr(0, 170) + '...' : text; +const newForDays = 100; + const addPluginDocsLink = '/docs/plugins/add-to-marketplace'; const defaultIconUrl = 'img/logo-gradient-on-dark.svg'; @@ -100,8 +119,15 @@ const Plugins = () => ( authorUrl, documentation, category, + date, }) => (
+ {Math.trunc((Date.now() - date) / (1000 * 60 * 60 * 24)) < + newForDays && ( +
+ NEW +
+ )}
{title} diff --git a/microsite/sidebars.json b/microsite/sidebars.json index a93e148317..0826541071 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -1,6 +1,7 @@ { "releases": { "Release Notes": [ + "releases/v1.7.0", "releases/v1.6.0", "releases/v1.5.0", "releases/v1.4.0", @@ -226,6 +227,7 @@ "plugins/composability", "plugins/customization", "plugins/analytics", + "plugins/feature-flags", { "type": "subcategory", "label": "Backends and APIs", diff --git a/microsite/static/css/plugins.css b/microsite/static/css/plugins.css index b9aebfa84c..55a0c6df40 100644 --- a/microsite/static/css/plugins.css +++ b/microsite/static/css/plugins.css @@ -4,6 +4,7 @@ padding: 16px; display: flex; flex-direction: column; + position: relative; } .PluginGrid { @@ -141,3 +142,31 @@ #add-plugin-card { border: 1px solid #69ddc7; } + +/* RIBBON */ + +.ribbon { + width: 80px; + height: 80px; + overflow: hidden; + position: absolute; +} +.ribbon span { + position: absolute; + display: block; + width: 140px; + padding: 4px 0; + background-color: rgb(54, 186, 162, 0.85); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1); + color: #fff; + text-align: center; +} +.ribbon-top-right { + top: 0; + right: 0; +} +.ribbon-top-right span { + left: -17px; + top: 11px; + transform: rotate(45deg); +} diff --git a/package.json b/package.json index 40e2056e8c..85c4fa6e23 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "build": "backstage-cli repo build --all", "build:api-reports": "yarn build:api-reports:only --tsc", "build:api-reports:only": "ts-node -T -P scripts/tsconfig.json scripts/api-extractor.ts", - "build:api-docs": "yarn build:api-reports --docs", + "build:api-docs": "LANG=en_EN yarn build:api-reports --docs", "tsc": "tsc", "tsc:full": "backstage-cli repo clean && tsc --skipLibCheck false --incremental false", "clean": "backstage-cli repo clean", @@ -44,8 +44,9 @@ "@types/react": "^17", "@types/react-dom": "^17" }, - "version": "1.7.0-next.1", + "version": "1.7.0", "dependencies": { + "@backstage/errors": "workspace:^", "@manypkg/get-packages": "^1.1.3", "@microsoft/api-documenter": "^7.17.11", "@microsoft/api-extractor": "^7.23.0", diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md index c4082ad530..2dcaaa362a 100644 --- a/packages/app-defaults/CHANGELOG.md +++ b/packages/app-defaults/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/app-defaults +## 1.0.7 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-app-api@1.1.1 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + - @backstage/plugin-permission-react@0.4.6 + +## 1.0.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-react@0.4.6-next.2 + - @backstage/core-app-api@1.1.1-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + ## 1.0.7-next.1 ### Patch Changes diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index 476509730a..de27f276a6 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/app-defaults", "description": "Provides the default wiring of a Backstage App", - "version": "1.0.7-next.1", + "version": "1.0.7", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index d492b19a1a..d0c822dd1e 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,129 @@ # example-app +## 0.2.76 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-user-settings@0.5.0 + - @backstage/plugin-scaffolder@1.7.0 + - @backstage/plugin-org@0.5.10 + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/plugin-catalog-graph@0.2.22 + - @backstage/core-app-api@1.1.1 + - @backstage/cli@0.20.0 + - @backstage/plugin-catalog-common@1.0.7 + - @backstage/plugin-catalog-import@0.9.0 + - @backstage/plugin-tech-insights@0.3.1 + - @backstage/plugin-kubernetes@0.7.3 + - @backstage/plugin-gcalendar@0.3.6 + - @backstage/plugin-api-docs@0.8.10 + - @backstage/plugin-search-react@1.2.0 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-cost-insights@0.11.32 + - @backstage/plugin-airbrake@0.3.10 + - @backstage/plugin-azure-devops@0.2.1 + - @backstage/plugin-badges@0.2.34 + - @backstage/plugin-circleci@0.3.10 + - @backstage/plugin-cloudbuild@0.3.10 + - @backstage/plugin-code-coverage@0.2.3 + - @backstage/plugin-dynatrace@1.0.0 + - @backstage/plugin-explore@0.3.41 + - @backstage/plugin-github-actions@0.5.10 + - @backstage/plugin-gocd@0.1.16 + - @backstage/plugin-home@0.4.26 + - @backstage/plugin-jenkins@0.7.9 + - @backstage/plugin-kafka@0.3.10 + - @backstage/plugin-lighthouse@0.3.10 + - @backstage/plugin-newrelic-dashboard@0.2.3 + - @backstage/plugin-pagerduty@0.5.3 + - @backstage/plugin-playlist@0.1.1 + - @backstage/plugin-rollbar@0.4.10 + - @backstage/plugin-search@1.0.3 + - @backstage/plugin-sentry@0.4.3 + - @backstage/plugin-techdocs@1.3.3 + - @backstage/plugin-techdocs-react@1.0.5 + - @backstage/plugin-todo@0.2.12 + - @internal/plugin-catalog-customized@0.0.3 + - @backstage/app-defaults@1.0.7 + - @backstage/integration-react@1.1.5 + - @backstage/plugin-apache-airflow@0.2.3 + - @backstage/plugin-gcp-projects@0.3.29 + - @backstage/plugin-graphiql@0.2.42 + - @backstage/plugin-newrelic@0.3.28 + - @backstage/plugin-shortcuts@0.3.2 + - @backstage/plugin-stack-overflow@0.1.6 + - @backstage/plugin-tech-radar@0.5.17 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/theme@0.2.16 + - @backstage/plugin-permission-react@0.4.6 + +## 0.2.76-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder@1.7.0-next.2 + - @backstage/plugin-catalog-common@1.0.7-next.2 + - @backstage/plugin-catalog-import@0.9.0-next.2 + - @backstage/cli@0.20.0-next.2 + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/plugin-playlist@0.1.1-next.2 + - @backstage/plugin-permission-react@0.4.6-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/app-defaults@1.0.7-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-app-api@1.1.1-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-airbrake@0.3.10-next.2 + - @backstage/plugin-apache-airflow@0.2.3-next.2 + - @backstage/plugin-api-docs@0.8.10-next.2 + - @backstage/plugin-azure-devops@0.2.1-next.2 + - @backstage/plugin-badges@0.2.34-next.2 + - @backstage/plugin-catalog-graph@0.2.22-next.2 + - @backstage/plugin-circleci@0.3.10-next.2 + - @backstage/plugin-cloudbuild@0.3.10-next.2 + - @backstage/plugin-code-coverage@0.2.3-next.2 + - @backstage/plugin-cost-insights@0.11.32-next.2 + - @backstage/plugin-dynatrace@1.0.0-next.2 + - @backstage/plugin-explore@0.3.41-next.2 + - @backstage/plugin-gcalendar@0.3.6-next.2 + - @backstage/plugin-gcp-projects@0.3.29-next.2 + - @backstage/plugin-github-actions@0.5.10-next.2 + - @backstage/plugin-gocd@0.1.16-next.2 + - @backstage/plugin-graphiql@0.2.42-next.2 + - @backstage/plugin-home@0.4.26-next.2 + - @backstage/plugin-jenkins@0.7.9-next.2 + - @backstage/plugin-kafka@0.3.10-next.2 + - @backstage/plugin-kubernetes@0.7.3-next.2 + - @backstage/plugin-lighthouse@0.3.10-next.2 + - @backstage/plugin-newrelic@0.3.28-next.2 + - @backstage/plugin-newrelic-dashboard@0.2.3-next.2 + - @backstage/plugin-org@0.5.10-next.2 + - @backstage/plugin-pagerduty@0.5.3-next.2 + - @backstage/plugin-rollbar@0.4.10-next.2 + - @backstage/plugin-search@1.0.3-next.2 + - @backstage/plugin-search-react@1.2.0-next.2 + - @backstage/plugin-sentry@0.4.3-next.2 + - @backstage/plugin-shortcuts@0.3.2-next.2 + - @backstage/plugin-stack-overflow@0.1.6-next.2 + - @backstage/plugin-tech-insights@0.3.1-next.2 + - @backstage/plugin-tech-radar@0.5.17-next.2 + - @backstage/plugin-techdocs@1.3.3-next.2 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.5-next.2 + - @backstage/plugin-techdocs-react@1.0.5-next.2 + - @backstage/plugin-todo@0.2.12-next.2 + - @backstage/plugin-user-settings@0.5.0-next.2 + - @internal/plugin-catalog-customized@0.0.3-next.2 + ## 0.2.76-next.1 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 76e099bace..441bdd1138 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "example-app", - "version": "0.2.76-next.1", + "version": "0.2.76", "private": true, "backstage": { "role": "frontend" @@ -19,6 +19,7 @@ "@backstage/plugin-apache-airflow": "workspace:^", "@backstage/plugin-api-docs": "workspace:^", "@backstage/plugin-azure-devops": "workspace:^", + "@backstage/plugin-azure-sites": "workspace:^", "@backstage/plugin-badges": "workspace:^", "@backstage/plugin-catalog-common": "workspace:^", "@backstage/plugin-catalog-graph": "workspace:^", @@ -83,7 +84,6 @@ }, "devDependencies": { "@backstage/test-utils": "workspace:^", - "@rjsf/core": "^3.2.1", "@testing-library/cypress": "^8.0.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index d6b66d57a7..2526f6740f 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -150,7 +150,7 @@ const AppRouter = app.getRouter(); const routes = ( - } /> + } /> {/* TODO(rubenl): Move this to / once its more mature and components exist */} }> {homePage} diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index f7592eb0b9..8e72bb03e4 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -209,7 +209,7 @@ const techdocsContent = ( /** * NOTE: This page is designed to work on small screens such as mobile devices. * This is based on Material UI Grid. If breakpoints are used, each grid item must set the `xs` prop to a column size or to `true`, - * since this does not default. If no breakpoints are used, the items will equitably share the asvailable space. + * since this does not default. If no breakpoints are used, the items will equitably share the available space. * https://material-ui.com/components/grid/#basic-grid. */ diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index 99814130c2..d324b683d8 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -35,6 +35,7 @@ import { SearchBar, SearchFilter, SearchResult, + SearchPagination, SearchResultPager, useSearch, } from '@backstage/plugin-search-react'; @@ -129,6 +130,7 @@ const SearchPage = () => { )} + {({ results }) => ( diff --git a/packages/backend-app-api/CHANGELOG.md b/packages/backend-app-api/CHANGELOG.md index 3823d025ac..9db48afd49 100644 --- a/packages/backend-app-api/CHANGELOG.md +++ b/packages/backend-app-api/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/backend-app-api +## 0.2.2 + +### Patch Changes + +- 0027a749cd: Added possibility to configure index plugin of the HTTP router service. +- 45857bffae: Properly export `rootLoggerFactory`. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/backend-tasks@0.3.6 + - @backstage/plugin-permission-node@0.7.0 + - @backstage/backend-plugin-api@0.1.3 + - @backstage/errors@1.1.2 + +## 0.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-node@0.7.0-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + - @backstage/errors@1.1.2-next.2 + ## 0.2.2-next.1 ### Patch Changes diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index c454785aa2..04a03677c0 100644 --- a/packages/backend-app-api/package.json +++ b/packages/backend-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-app-api", "description": "Core API used by Backstage backend apps", - "version": "0.2.2-next.1", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index 143b6d28c9..c6211b05b4 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/backend-common +## 0.15.2 + +### Patch Changes + +- e8d7976413: Added back support for when no branch is provided for the Bitbucket Server `UrlReader` +- c44cf412de: Fix BitBucket server integration +- c31f7cdfbc: Fixed an issue where `getClient()` for a `pluginId` would return different clients and not share them +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3 + - @backstage/config-loader@1.1.5 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## 0.15.2-next.2 + +### Patch Changes + +- c44cf412de: Fix BitBucket server integration +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3-next.2 + - @backstage/config-loader@1.1.5-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + ## 0.15.2-next.1 ### Patch Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index b6b0c5e35a..2a7e307774 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.15.2-next.1", + "version": "0.15.2", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-common/src/reading/BitbucketServerUrlReader.test.ts b/packages/backend-common/src/reading/BitbucketServerUrlReader.test.ts index 15f1c0d4be..9f231e2ba7 100644 --- a/packages/backend-common/src/reading/BitbucketServerUrlReader.test.ts +++ b/packages/backend-common/src/reading/BitbucketServerUrlReader.test.ts @@ -102,6 +102,25 @@ describe('BitbucketServerUrlReader', () => { }), ), ), + rest.get( + 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/branches/default', + (_, res, ctx) => + res( + ctx.status(200), + ctx.json({ + id: 'refs/heads/master', + displayId: 'master', + type: 'BRANCH', + latestCommit: '3bdd5457286abdf920db4b77bf2fef79a06190c2', + latestChangeset: '3bdd5457286abdf920db4b77bf2fef79a06190c2', + isDefault: true, + }), + ), + ), + rest.get( + 'https://api.bitbucket.mycompany.net/rest/api/1.0/projects/backstage/repos/mock/default-branch', + (_, res, ctx) => res(ctx.status(404)), + ), ); }); @@ -119,6 +138,14 @@ describe('BitbucketServerUrlReader', () => { expect(indexMarkdownFile.toString()).toBe('# Test\n'); }); + + it('uses default branch when no branch is provided', async () => { + const response = await reader.readTree( + 'https://bitbucket.mycompany.net/projects/backstage/repos/mock/browse/src', + ); + + expect(response.etag).toBe('3bdd5457286a'); + }); }); describe('readTree without branch', () => { diff --git a/packages/backend-common/src/reading/BitbucketServerUrlReader.ts b/packages/backend-common/src/reading/BitbucketServerUrlReader.ts index 98f95b88cb..bcab226b0f 100644 --- a/packages/backend-common/src/reading/BitbucketServerUrlReader.ts +++ b/packages/backend-common/src/reading/BitbucketServerUrlReader.ts @@ -188,11 +188,13 @@ export class BitbucketServerUrlReader implements UrlReader { private async getLastCommitShortHash(url: string): Promise { const { name: repoName, owner: project, ref: branch } = parseGitUrl(url); - const branchListUrl = `${ - this.integration.config.apiBaseUrl - }/projects/${project}/repos/${repoName}/branches?filterText=${encodeURIComponent( - branch, - )}`; + // If a branch is provided use that otherwise fall back to the default branch + const branchParameter = branch + ? `?filterText=${encodeURIComponent(branch)}` + : '/default'; + + // https://docs.atlassian.com/bitbucket-server/rest/7.9.0/bitbucket-rest.html#idp211 (branches docs) + const branchListUrl = `${this.integration.config.apiBaseUrl}/projects/${project}/repos/${repoName}/branches${branchParameter}`; const branchListResponse = await fetch( branchListUrl, @@ -216,8 +218,15 @@ export class BitbucketServerUrlReader implements UrlReader { return exactBranchMatch.latestCommit.substring(0, 12); } + // Handle when no branch is provided using the default as the fallback + if (!branch && branchMatches) { + return branchMatches.latestCommit.substring(0, 12); + } + throw new Error( - `Failed to find branch "${branch}" in property "displayId" of response to ${branchListUrl}`, + `Failed to find Last Commit using ${ + branch ? `branch "${branch}"` : 'default branch' + } in response from ${branchListUrl}`, ); } } diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index 8d980fe1bc..a569ff349c 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/backend-defaults +## 0.1.2 + +### Patch Changes + +- 96d288a02d: Added root logger service to the set of default services. +- Updated dependencies + - @backstage/backend-app-api@0.2.2 + - @backstage/backend-plugin-api@0.1.3 + +## 0.1.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.2.2-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + ## 0.1.2-next.1 ### Patch Changes diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index 1d0a3f09f6..4de7123983 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-defaults", "description": "Backend defaults used by Backstage backend apps", - "version": "0.1.2-next.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-next/CHANGELOG.md b/packages/backend-next/CHANGELOG.md index e8d6cefa1b..3315f4a27f 100644 --- a/packages/backend-next/CHANGELOG.md +++ b/packages/backend-next/CHANGELOG.md @@ -1,5 +1,25 @@ # example-backend-next +## 0.0.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/plugin-scaffolder-backend@1.7.0 + - @backstage/backend-defaults@0.1.2 + - @backstage/plugin-app-backend@0.3.37 + +## 0.0.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/plugin-scaffolder-backend@1.7.0-next.2 + - @backstage/plugin-app-backend@0.3.37-next.2 + - @backstage/backend-defaults@0.1.2-next.2 + ## 0.0.4-next.1 ### Patch Changes diff --git a/packages/backend-next/package.json b/packages/backend-next/package.json index 7db8ef9775..f82f2fc4fc 100644 --- a/packages/backend-next/package.json +++ b/packages/backend-next/package.json @@ -1,6 +1,6 @@ { "name": "example-backend-next", - "version": "0.0.4-next.1", + "version": "0.0.4", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/backend-plugin-api/CHANGELOG.md b/packages/backend-plugin-api/CHANGELOG.md index 21897d0a97..c45f0a968e 100644 --- a/packages/backend-plugin-api/CHANGELOG.md +++ b/packages/backend-plugin-api/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/backend-plugin-api +## 0.1.3 + +### Patch Changes + +- 28377dc89f: Allow interfaces to be used for inferred option types. +- a35a27df70: Added documentation for `createBackendModule`, with guidelines for choosing a module ID. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/backend-tasks@0.3.6 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/config@1.0.3 + +## 0.1.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/config@1.0.3-next.2 + ## 0.1.3-next.1 ### Patch Changes diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index 80e3a8adfa..59e1de9529 100644 --- a/packages/backend-plugin-api/package.json +++ b/packages/backend-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-plugin-api", "description": "Core API used by Backstage backend plugins", - "version": "0.1.3-next.1", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-tasks/CHANGELOG.md b/packages/backend-tasks/CHANGELOG.md index 97468f3856..87d3acf503 100644 --- a/packages/backend-tasks/CHANGELOG.md +++ b/packages/backend-tasks/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/backend-tasks +## 0.3.6 + +### Patch Changes + +- d4fea86ea3: Added new function `readTaskScheduleDefinitionFromConfig` to read `TaskScheduleDefinition` (aka. schedule) from the `Config`. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## 0.3.6-next.2 + +### Patch Changes + +- d4fea86ea3: Added new function `readTaskScheduleDefinitionFromConfig` to read `TaskScheduleDefinition` (aka. schedule) from the `Config`. +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + ## 0.3.6-next.1 ### Patch Changes diff --git a/packages/backend-tasks/package.json b/packages/backend-tasks/package.json index 9fc7d6cbae..6c10478e6c 100644 --- a/packages/backend-tasks/package.json +++ b/packages/backend-tasks/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-tasks", "description": "Common distributed task management library for Backstage backends", - "version": "0.3.6-next.1", + "version": "0.3.6", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 753ad24289..e32a82bab8 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/backend-test-utils +## 0.1.29 + +### Patch Changes + +- 72549952d1: Fixed handling of root scoped services in `startTestBackend`. +- e91e8e9c55: Increased test database max connection pool size to reduce the risk of resource exhaustion. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/cli@0.20.0 + - @backstage/backend-app-api@0.2.2 + - @backstage/backend-plugin-api@0.1.3 + - @backstage/config@1.0.3 + +## 0.1.29-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/cli@0.20.0-next.2 + - @backstage/backend-app-api@0.2.2-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + - @backstage/config@1.0.3-next.2 + ## 0.1.29-next.1 ### Patch Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 5249196860..aeedaec8e3 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-test-utils", "description": "Test helpers library for Backstage backends", - "version": "0.1.29-next.1", + "version": "0.1.29", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index c5dc2f71bd..e519556f7f 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,89 @@ # example-backend +## 0.2.76 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/plugin-scaffolder-backend@1.7.0 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/backend-tasks@0.3.6 + - @backstage/plugin-permission-node@0.7.0 + - @backstage/plugin-auth-backend@0.17.0 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/plugin-tech-insights-backend@0.5.3 + - @backstage/plugin-search-backend@1.1.0 + - @backstage/catalog-client@1.1.1 + - @backstage/plugin-playlist-backend@0.2.0 + - @backstage/plugin-jenkins-backend@0.1.27 + - @backstage/plugin-app-backend@0.3.37 + - @backstage/plugin-badges-backend@0.1.31 + - @backstage/plugin-graphql-backend@0.1.27 + - @backstage/plugin-permission-backend@0.5.12 + - @backstage/plugin-rollbar-backend@0.1.34 + - @backstage/plugin-kubernetes-backend@0.7.3 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-search-backend-node@1.0.3 + - @backstage/plugin-search-backend-module-pg@0.4.1 + - @backstage/plugin-search-backend-module-elasticsearch@1.0.3 + - @backstage/plugin-techdocs-backend@1.4.0 + - @backstage/plugin-tech-insights-node@0.3.5 + - example-app@0.2.76 + - @backstage/plugin-code-coverage-backend@0.2.3 + - @backstage/plugin-kafka-backend@0.2.30 + - @backstage/plugin-todo-backend@0.1.34 + - @backstage/plugin-azure-devops-backend@0.3.16 + - @backstage/plugin-proxy-backend@0.2.31 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.5 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.21 + - @backstage/config@1.0.3 + - @backstage/integration@1.3.2 + +## 0.2.76-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-permission-node@0.7.0-next.2 + - @backstage/plugin-scaffolder-backend@1.7.0-next.2 + - @backstage/plugin-playlist-backend@0.2.0-next.2 + - @backstage/plugin-badges-backend@0.1.31-next.2 + - @backstage/plugin-graphql-backend@0.1.27-next.2 + - @backstage/plugin-permission-backend@0.5.12-next.2 + - @backstage/plugin-rollbar-backend@0.1.34-next.2 + - @backstage/plugin-search-backend@1.1.0-next.2 + - @backstage/plugin-tech-insights-backend@0.5.3-next.2 + - @backstage/plugin-techdocs-backend@1.4.0-next.2 + - example-app@0.2.76-next.2 + - @backstage/plugin-search-backend-node@1.0.3-next.2 + - @backstage/plugin-tech-insights-node@0.3.5-next.2 + - @backstage/plugin-app-backend@0.3.37-next.2 + - @backstage/plugin-auth-backend@0.17.0-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/plugin-azure-devops-backend@0.3.16-next.2 + - @backstage/plugin-code-coverage-backend@0.2.3-next.2 + - @backstage/plugin-jenkins-backend@0.1.27-next.2 + - @backstage/plugin-kafka-backend@0.2.30-next.2 + - @backstage/plugin-kubernetes-backend@0.7.3-next.2 + - @backstage/plugin-proxy-backend@0.2.31-next.2 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.5-next.2 + - @backstage/plugin-search-backend-module-elasticsearch@1.0.3-next.2 + - @backstage/plugin-search-backend-module-pg@0.4.1-next.2 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.21-next.2 + - @backstage/plugin-todo-backend@0.1.34-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/integration@1.3.2-next.2 + ## 0.2.76-next.1 ### Patch Changes diff --git a/packages/backend/Dockerfile b/packages/backend/Dockerfile index 21ff07deb4..60c6ffd27f 100644 --- a/packages/backend/Dockerfile +++ b/packages/backend/Dockerfile @@ -13,9 +13,10 @@ FROM node:16-bullseye-slim # Install sqlite3 dependencies. You can skip this if you don't use sqlite3 in the image, # in which case you should also move better-sqlite3 to "devDependencies" in package.json. -RUN apt-get update && \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update && \ apt-get install -y --no-install-recommends libsqlite3-dev python3 build-essential && \ - rm -rf /var/lib/apt/lists/* && \ yarn config set python /usr/bin/python3 # From here on we use the least-privileged `node` user to run the backend. @@ -38,7 +39,8 @@ COPY --chown=node:node yarn.lock package.json packages/backend/dist/skeleton.tar RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz # Note that this install is not immutable, which is one of the reasons we don't recommend Yarn 3 yet -RUN yarn workspaces focus --all --production && rm -rf "$(yarn cache clean)" +RUN --mount=type=cache,target=/home/node/.yarn/berry/cache,sharing=locked,uid=1000,gid=1000 \ + yarn workspaces focus --all --production # Then copy the rest of the backend bundle, along with any other files we might want. COPY --chown=node:node packages/backend/dist/bundle.tar.gz app-config*.yaml ./ diff --git a/packages/backend/package.json b/packages/backend/package.json index 75a116ee2a..4e296616fc 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.76-next.1", + "version": "0.2.76", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", @@ -36,6 +36,7 @@ "@backstage/plugin-auth-backend": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/plugin-azure-devops-backend": "workspace:^", + "@backstage/plugin-azure-sites-backend": "workspace:^", "@backstage/plugin-badges-backend": "workspace:^", "@backstage/plugin-catalog-backend": "workspace:^", "@backstage/plugin-code-coverage-backend": "workspace:^", diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index 0e5a847703..fbd49ff49e 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/catalog-client +## 1.1.1 + +### Patch Changes + +- 4f2ac624b4: Renamed argument in `validateEntity` from `location` to `locationRef` +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/errors@1.1.2 + +## 1.1.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/errors@1.1.2-next.2 + ## 1.1.1-next.1 ### Patch Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index 31b8654556..9c4e79b079 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/catalog-client", "description": "An isomorphic client for the catalog backend", - "version": "1.1.1-next.1", + "version": "1.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index 6352f13760..2ac459101c 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/catalog-model +## 1.1.2 + +### Patch Changes + +- 6f3b8d0962: Defer `ajv` compilation of schema validators to improve module-import performance +- Updated dependencies + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## 1.1.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + ## 1.1.2-next.1 ### Patch Changes diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index b1a83fbeb0..91016d6a33 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/catalog-model", "description": "Types and validators that help describe the model of a Backstage Catalog", - "version": "1.1.2-next.1", + "version": "1.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 90caca8f18..ad63532139 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,70 @@ # @backstage/cli +## 0.20.0 + +### Minor Changes + +- f368ad7279: **BREAKING**: Bumped `jest`, `jest-runtime`, and `jest-environment-jsdom` to v29. This is up from v27, so check out both the [v28](https://jestjs.io/docs/28.x/upgrading-to-jest28) and [v29](https://jestjs.io/docs/upgrading-to-jest29) (later [here](https://jestjs.io/docs/29.x/upgrading-to-jest29)) migration guides. + + Particular changes that where encountered in the main Backstage repo are: + + - The updated snapshot format. + - `jest.useFakeTimers('legacy')` is now `jest.useFakeTimers({ legacyFakeTimers: true })`. + - Error objects collected by `withLogCollector` from `@backstage/test-utils` are now objects with a `detail` property rather than a string. + +### Patch Changes + +- 78d5eb299e: Tweak the Jest Caching loader to only operate when in `watch` mode +- 9c595302cb: Normalize on winston version ^3.2.1 +- 24b40140c4: Treat files in `__testUtils__` and `__mocks__` directories as test files for linting + purposes. + + Updates the parts of the eslint configuration generator that specify which files + should be treated as test code to include any files under two additional + directories: + + - `__mocks__`: this is the directory used by Jest[0] for mock code. + - `__testUtils__`: a suggested location for utility code executed only when + running tests. + + [0]: https://jestjs.io/docs/manual-mocks#mocking-user-modules + +- 3e309107ca: Updated fallback versions of dependencies in all templates. +- 292a088807: Added a new `repo test` command. +- ba63cae41c: Updated lockfile parsing to have better support for Yarn 3. +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- 2dddb32fea: Switched the Jest transform for YAML files to use a custom one available at `@backstage/cli/config/jestYamlTransform.js`. +- a541a3a78a: Switch to upfront resolution of `swc-loader` in Webpack config. +- cfb3598410: Removed `tsx` and `jsx` as supported extensions in backend packages. For most + repos, this will not have any effect. But if you inadvertently had added some + `tsx`/`jsx` files to your backend package, you may now start to see `code: 'MODULE_NOT_FOUND'` errors when launching the backend locally. The reason for + this is that the offending files get ignored during transpilation. Hence, the + importing file can no longer find anything to import. + + The fix is to rename any `.tsx` files in your backend packages to `.ts` instead, + or `.jsx` to `.js`. + +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3 + - @backstage/config-loader@1.1.5 + - @backstage/errors@1.1.2 + - @backstage/release-manifests@0.0.6 + - @backstage/types@1.0.0 + +## 0.20.0-next.2 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3-next.2 + - @backstage/config-loader@1.1.5-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/release-manifests@0.0.6 + - @backstage/types@1.0.0 + ## 0.20.0-next.1 ### Patch Changes diff --git a/packages/cli/config/eslint-factory.js b/packages/cli/config/eslint-factory.js index 6e2a510c4a..e213f58e4e 100644 --- a/packages/cli/config/eslint-factory.js +++ b/packages/cli/config/eslint-factory.js @@ -96,12 +96,16 @@ function createConfig(dir, extraConfig = {}) { `!${joinPath(dir, 'src/**')}`, joinPath(dir, 'src/**/*.test.*'), joinPath(dir, 'src/**/*.stories.*'), + joinPath(dir, 'src/**/__testUtils__/**'), + joinPath(dir, 'src/**/__mocks__/**'), joinPath(dir, 'src/setupTests.*'), ] : [ // Legacy config for packages that don't provide a dir '**/*.test.*', '**/*.stories.*', + '**/__testUtils__/**', + '**/__mocks__/**', '**/src/setupTests.*', '**/dev/**', ], @@ -137,6 +141,8 @@ function createConfig(dir, extraConfig = {}) { // Prevent imports of stories or tests '*.stories*', '*.test*', + '**/__testUtils__/**', + '**/__mocks__/**', ], }, ], @@ -159,7 +165,14 @@ function createConfig(dir, extraConfig = {}) { }, }, { - files: ['**/*.test.*', '**/*.stories.*', 'src/setupTests.*', '!src/**'], + files: [ + '**/*.test.*', + '**/*.stories.*', + '**/__testUtils__/**', + '**/__mocks__/**', + 'src/setupTests.*', + '!src/**', + ], rules: { ...testRules, 'no-restricted-syntax': [ diff --git a/packages/cli/package.json b/packages/cli/package.json index 66f69772d5..f460bfa4e0 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.20.0-next.1", + "version": "0.20.0", "publishConfig": { "access": "public" }, diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs index 0a72a5d415..df381159f0 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -33,7 +33,7 @@ "@types/express": "{{versionQuery '@types/express' '4.17.13'}}", "express": "{{versionQuery 'express' '4.18.1'}}", "express-promise-router": "{{versionQuery 'express-promise-router' '4.1.0'}}", - "winston": "{{versionQuery 'winston' '3.8.1'}}", + "winston": "{{versionQuery 'winston' '3.2.1'}}", "node-fetch": "{{versionQuery 'node-fetch' '2.6.7'}}", "yn": "{{versionQuery 'yn' '4.0.0'}}" }, diff --git a/packages/codemods/CHANGELOG.md b/packages/codemods/CHANGELOG.md index 673054c186..161a8dbdf9 100644 --- a/packages/codemods/CHANGELOG.md +++ b/packages/codemods/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/codemods +## 0.1.40 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.10 + +## 0.1.40-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.10 + ## 0.1.40-next.1 ### Patch Changes diff --git a/packages/codemods/package.json b/packages/codemods/package.json index 4996887eed..cc3ad6e468 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/codemods", "description": "A collection of codemods for Backstage projects", - "version": "0.1.40-next.1", + "version": "0.1.40", "publishConfig": { "access": "public", "main": "dist/index.cjs.js" diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index 0c1afa75c1..b5b243d788 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/config-loader +## 1.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## 1.1.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + ## 1.1.5-next.1 ### Patch Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 3fb1da9e1f..6a96e7841b 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config-loader", "description": "Config loading functionality used by Backstage backend, and CLI", - "version": "1.1.5-next.1", + "version": "1.1.5", "publishConfig": { "access": "public", "main": "dist/index.cjs.js", diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md index 151201bfa7..a03770f991 100644 --- a/packages/config/CHANGELOG.md +++ b/packages/config/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/config +## 1.0.3 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + +## 1.0.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + ## 1.0.3-next.1 ### Patch Changes diff --git a/packages/config/package.json b/packages/config/package.json index 2305784051..018aeb47cf 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config", "description": "Config API used by Backstage core, backend, and CLI", - "version": "1.0.3-next.1", + "version": "1.0.3", "publishConfig": { "access": "public", "main": "dist/index.cjs.js", diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index 8914390d89..147a3737cc 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/core-app-api +## 1.1.1 + +### Patch Changes + +- 27e6404aba: Fixed a bug where gathered index routes would fail to bind routable extensions. This would typically show up when placing a routable extension in the entity page overview tab. +- Updated dependencies + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/types@1.0.0 + - @backstage/version-bridge@1.0.1 + +## 1.1.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/types@1.0.0 + - @backstage/version-bridge@1.0.1 + ## 1.1.1-next.1 ### Patch Changes diff --git a/packages/core-app-api/config.d.ts b/packages/core-app-api/config.d.ts index d88c818d11..ae876f7fe0 100644 --- a/packages/core-app-api/config.d.ts +++ b/packages/core-app-api/config.d.ts @@ -89,6 +89,11 @@ export interface Config { name?: string; }; + /** + * This config was used by the HomepageTimer but has been replaced by the HeaderWorldClock in the home plugin + * + * @deprecated in favor of the HeaderWorldClock which is found in the home plugin + */ homepage?: { clocks?: Array<{ /** @visibility frontend */ diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 219244eacc..7d4b6eba30 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-app-api", "description": "Core app API used by Backstage apps", - "version": "1.1.1-next.1", + "version": "1.1.1", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index a27be20b9f..0d58a7032a 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/core-components +## 0.11.2 + +### Patch Changes + +- bde1e8c8e2: Added `curve` prop to the `DependencyGraph` component to select the type of layout +- 882101cd9b: Deep-import LightAsync component to improve module-import speed +- 0d0f30d87e: Fixed layout for core-components docs where table was broken by splitting with code sample +- Updated dependencies + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + - @backstage/version-bridge@1.0.1 + +## 0.11.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + - @backstage/version-bridge@1.0.1 + ## 0.11.2-next.1 ### Patch Changes diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 99d18b9b12..fc3ab4e5fc 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -242,6 +242,7 @@ export interface DependencyGraphProps acyclicer?: 'greedy'; // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver align?: Alignment; + curve?: 'curveStepBefore' | 'curveMonotoneX'; defs?: SVGDefsElement | SVGDefsElement[]; // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver direction?: Direction; @@ -494,9 +495,6 @@ export type HeaderTabsClassKey = // @public (undocumented) export function HelpIcon(props: IconComponentProps): JSX.Element; -// @public @deprecated -export function HomepageTimer(_props: {}): JSX.Element | null; - // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // // @public diff --git a/packages/core-components/package.json b/packages/core-components/package.json index a32d8c9af8..4c7e9fb4db 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-components", "description": "Core components used by Backstage plugins and apps", - "version": "0.11.2-next.1", + "version": "0.11.2", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index 9f447f80cc..5159f67d9e 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -162,6 +162,14 @@ export interface DependencyGraphProps * Default: `enabled` */ zoom?: 'enabled' | 'disabled' | 'enable-on-click'; + /** + * A factory for curve generators addressing both lines and areas. + * + * @remarks + * + * Default: 'curveMonotoneX' + */ + curve?: 'curveStepBefore' | 'curveMonotoneX'; } const WORKSPACE_ID = 'workspace'; @@ -194,6 +202,7 @@ export function DependencyGraph( renderLabel, defs, zoom = 'enabled', + curve = 'curveMonotoneX', ...svgProps } = props; const theme: BackstageTheme = useTheme(); @@ -424,6 +433,7 @@ export function DependencyGraph( setEdge={setEdge} render={renderLabel} edge={edge} + curve={curve} /> ); })} diff --git a/packages/core-components/src/components/DependencyGraph/Edge.test.tsx b/packages/core-components/src/components/DependencyGraph/Edge.test.tsx index db6fbe7552..21d677388e 100644 --- a/packages/core-components/src/components/DependencyGraph/Edge.test.tsx +++ b/packages/core-components/src/components/DependencyGraph/Edge.test.tsx @@ -21,6 +21,7 @@ import { RenderLabelProps } from './types'; const fromNode = 'node'; const toNode = 'other-node'; +const curve: 'curveStepBefore' | 'curveMonotoneX' = 'curveMonotoneX'; const edge = { points: [ @@ -46,6 +47,7 @@ const minProps = { setEdge, renderElement, edge, + curve, }; const label = 'label'; diff --git a/packages/core-components/src/components/DependencyGraph/Edge.tsx b/packages/core-components/src/components/DependencyGraph/Edge.tsx index cc6a044beb..aae737b9ea 100644 --- a/packages/core-components/src/components/DependencyGraph/Edge.tsx +++ b/packages/core-components/src/components/DependencyGraph/Edge.tsx @@ -25,7 +25,7 @@ import { DependencyEdge, LabelPosition, } from './types'; -import { ARROW_MARKER_ID, EDGE_TEST_ID, LABEL_TEST_ID } from './constants'; +import { EDGE_TEST_ID, LABEL_TEST_ID } from './constants'; import { DefaultLabel } from './DefaultLabel'; import dagre from 'dagre'; @@ -47,7 +47,7 @@ export type DependencyGraphEdgeClassKey = 'path' | 'label'; const useStyles = makeStyles( (theme: BackstageTheme) => ({ path: { - strokeWidth: 2, + strokeWidth: 1, stroke: theme.palette.textSubtle, fill: 'none', transition: `${theme.transitions.duration.shortest}ms`, @@ -70,23 +70,19 @@ export type EdgeComponentProps = { id: dagre.Edge, edge: DependencyEdge, ) => dagre.graphlib.Graph<{}>; + curve: 'curveStepBefore' | 'curveMonotoneX'; }; const renderDefault = (props: RenderLabelProps) => ( ); -const createPath = d3Shape - .line() - .x(d => d.x) - .y(d => d.y) - .curve(d3Shape.curveMonotoneX); - export function Edge({ render = renderDefault, setEdge, id, edge, + curve, }: EdgeComponentProps) { const { x = 0, y = 0, width, height, points } = edge; const labelProps: DependencyEdge = edge; @@ -114,6 +110,16 @@ export function Edge({ let path: string = ''; + const createPath = React.useMemo( + () => + d3Shape + .line() + .x(d => d.x) + .y(d => d.y) + .curve(d3Shape[curve]), + [curve], + ); + if (points) { const finitePoints = points.filter( (point: EdgePoint) => isFinite(point.x) && isFinite(point.y), @@ -124,12 +130,7 @@ export function Edge({ return ( <> {path && ( - + )} {labelProps.label ? ( (props: TableProps) { } data={typeof data === 'function' ? data : tableData} style={{ width: '100%' }} - localization={{ toolbar: { searchPlaceholder: 'Filter' } }} + localization={{ + toolbar: { searchPlaceholder: 'Filter', searchTooltip: 'Filter' }, + }} {...restProps} />
diff --git a/packages/core-components/src/components/WarningPanel/WarningPanel.tsx b/packages/core-components/src/components/WarningPanel/WarningPanel.tsx index 661bb1da22..2ddcef52ca 100644 --- a/packages/core-components/src/components/WarningPanel/WarningPanel.tsx +++ b/packages/core-components/src/components/WarningPanel/WarningPanel.tsx @@ -134,7 +134,7 @@ const capitalize = (s: string) => { }; /** - * WarningPanel. Show a user friendly error message to a user similar to + * Show a user friendly error message to a user similar to * ErrorPanel except that the warning panel only shows the warning message to * the user. * diff --git a/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx b/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx deleted file mode 100644 index 9a7a8ac47d..0000000000 --- a/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx +++ /dev/null @@ -1,56 +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 { - renderWithEffects, - TestApiProvider, - withLogCollector, -} from '@backstage/test-utils'; -import { HomepageTimer } from './HomepageTimer'; -import React from 'react'; -import { lightTheme } from '@backstage/theme'; -import { ThemeProvider } from '@material-ui/core/styles'; -import { ConfigReader } from '@backstage/core-app-api'; -import { ConfigApi, configApiRef } from '@backstage/core-plugin-api'; - -it('changes default timezone to GMT', async () => { - const configApi: ConfigApi = new ConfigReader({ - homepage: { - clocks: [ - { - label: 'New York', - timezone: 'America/New_Pork', - }, - ], - }, - context: 'test', - }); - - const { warn } = await withLogCollector(async () => { - const rendered = await renderWithEffects( - - - - - , - ); - - expect(rendered.getByText('GMT')).toBeInTheDocument(); - }); - expect(warn).toEqual([ - 'The timezone America/New_Pork is invalid. Defaulting to GMT', - ]); -}); diff --git a/packages/core-components/src/layout/HomepageTimer/HomepageTimer.tsx b/packages/core-components/src/layout/HomepageTimer/HomepageTimer.tsx deleted file mode 100644 index 71e764987b..0000000000 --- a/packages/core-components/src/layout/HomepageTimer/HomepageTimer.tsx +++ /dev/null @@ -1,108 +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 React from 'react'; -import { HeaderLabel } from '../HeaderLabel'; -import { ConfigApi, useApi, configApiRef } from '@backstage/core-plugin-api'; - -const timeFormat: Intl.DateTimeFormatOptions = { - hour: '2-digit', - minute: '2-digit', -}; - -type TimeObj = { - time: string; - label: string; -}; - -function getTimes(configApi: ConfigApi) { - const d = new Date(); - const lang = window.navigator.language; - - const clocks: TimeObj[] = []; - - if (!configApi.has('homepage.clocks')) { - return clocks; - } - - const clockConfigs = configApi.getConfigArray('homepage.clocks'); - - for (const clock of clockConfigs) { - if (clock.has('label') && clock.has('timezone')) { - let label = clock.getString('label'); - - const options: Intl.DateTimeFormatOptions = { - timeZone: clock.getString('timezone'), - ...timeFormat, - }; - - try { - new Date().toLocaleString(lang, options); - } catch (e) { - // eslint-disable-next-line no-console - console.warn( - `The timezone ${options.timeZone} is invalid. Defaulting to GMT`, - ); - options.timeZone = 'GMT'; - label = 'GMT'; - } - - const time = d.toLocaleTimeString(lang, options); - clocks.push({ time, label }); - } - } - return clocks; -} - -/** - * Please use the HeaderWorldClock in the home plugin - * - * @public - * @deprecated in favor of the HeaderWorldClock which is found in the to home plugin - */ -export function HomepageTimer(_props: {}) { - const configApi = useApi(configApiRef); - - const defaultTimes: TimeObj[] = []; - const [clocks, setTimes] = React.useState(defaultTimes); - - React.useEffect(() => { - setTimes(getTimes(configApi)); - - const intervalId = setInterval(() => { - setTimes(getTimes(configApi)); - }, 1000); - - return () => { - clearInterval(intervalId); - }; - }, [configApi]); - - if (clocks.length !== 0) { - return ( - <> - {clocks.map(clock => ( - - ))} - - ); - } - return null; -} diff --git a/packages/core-components/src/layout/ItemCard/ItemCard.tsx b/packages/core-components/src/layout/ItemCard/ItemCard.tsx index e2efd0c136..fedcf7de8f 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCard.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCard.tsx @@ -40,6 +40,7 @@ type ItemCardProps = { * This card type has been deprecated. Instead use plain MUI Card and helpers * where appropriate. * + * @example * ``` * * diff --git a/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx b/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx index b27d224418..885b43ca34 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx @@ -49,6 +49,7 @@ export type ItemCardGridProps = Partial> & { * A default grid to use when arranging "item cards" - cards that let users * select among several options. * + * @remarks * The immediate children are expected to be MUI Card components. * * Styles for the grid can be overridden using the `classes` prop, e.g.: diff --git a/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx b/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx index d9443ffc9a..76cf668079 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx @@ -64,6 +64,7 @@ export type ItemCardHeaderProps = Partial> & { * A simple card header, rendering a default look for "item cards" - cards that * are arranged in a grid for users to select among several options. * + * @remarks * This component expects to be placed within a MUI ``. * * Styles for the header can be overridden using the `classes` prop, e.g.: diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 5a0b92f558..4bc972e272 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -510,6 +510,7 @@ const SidebarItemWithSubmenu = ({ /** * Creates a `SidebarItem` * + * @remarks * If children contain a `SidebarSubmenu` component the `SidebarItem` will have a expandable submenu */ export const SidebarItem = forwardRef((props, ref) => { @@ -663,6 +664,8 @@ export const SidebarScrollWrapper = styled('div')(({ theme }) => { /** * A button which allows you to expand the sidebar when clicked. + * + * @remarks * Use optionally to replace sidebar's expand-on-hover feature with expand-on-click. * * If you are using this you might want to set the `disableExpandOnHover` of the `Sidebar` to `true`. diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index c85e6d11f2..bfa841690d 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -159,6 +159,7 @@ export const MobileSidebarContext = createContext({ /** * A navigation component for mobile screens, which sticks to the bottom. * + * @remarks * It alternates the normal sidebar by grouping the `SidebarItems` based on provided `SidebarGroup`s * either rendering them as a link or an overlay menu. * If no `SidebarGroup`s are provided the sidebar content is wrapped in an default overlay menu. diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 1158cc14df..cb9c786ea9 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -115,6 +115,7 @@ const MobileSidebarGroup = (props: SidebarGroupProps) => { /** * Groups items of the `Sidebar` together. * + * @remarks * On bigger screens, this won't have any effect at the moment. * On small screens, it will add an action to the bottom navigation - either triggering an overlay menu or acting as a link * diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx index a1565a348f..51092d7e81 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx @@ -111,6 +111,7 @@ export type SidebarSubmenuItemDropdownItem = { /** * Holds submenu item content. * + * @remarks * title: Text content of submenu item * subtitle: A subtitle displayed under the main title * to: Path to navigate to when item is clicked diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index bb0ff207fb..dacf4bd0a5 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -20,13 +20,13 @@ const drawerWidthClosed = 72; const iconPadding = 24; const userBadgePadding = 18; -/** @public **/ +/** @public */ export type SidebarOptions = { drawerWidthClosed?: number; drawerWidthOpen?: number; }; -/** @public **/ +/** @public */ export type SubmenuOptions = { drawerWidthClosed?: number; drawerWidthOpen?: number; diff --git a/packages/core-components/src/layout/SignInPage/types.ts b/packages/core-components/src/layout/SignInPage/types.ts index 0909e7242d..8a0430a27d 100644 --- a/packages/core-components/src/layout/SignInPage/types.ts +++ b/packages/core-components/src/layout/SignInPage/types.ts @@ -32,7 +32,7 @@ export type SignInProviderConfig = { apiRef: ApiRef; }; -/** @public **/ +/** @public */ export type IdentityProviders = ('guest' | 'custom' | SignInProviderConfig)[]; export type ProviderComponent = ComponentType< diff --git a/packages/core-components/src/layout/index.ts b/packages/core-components/src/layout/index.ts index 4b594f0e94..a024cfbbc4 100644 --- a/packages/core-components/src/layout/index.ts +++ b/packages/core-components/src/layout/index.ts @@ -23,7 +23,6 @@ export * from './Header'; export * from './HeaderActionMenu'; export * from './HeaderLabel'; export * from './HeaderTabs'; -export * from './HomepageTimer'; export * from './InfoCard'; export * from './ItemCard'; export * from './Page'; diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index 26e18ea244..8b167be430 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/core-plugin-api +## 1.0.7 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3 + - @backstage/types@1.0.0 + - @backstage/version-bridge@1.0.1 + +## 1.0.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/types@1.0.0 + - @backstage/version-bridge@1.0.1 + ## 1.0.7-next.1 ### Patch Changes diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 48b78a1daf..1d8e21344c 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-plugin-api", "description": "Core API used by Backstage plugins", - "version": "1.0.7-next.1", + "version": "1.0.7", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/core-plugin-api/src/routing/SubRouteRef.ts b/packages/core-plugin-api/src/routing/SubRouteRef.ts index 61d89fcbdb..14a614c2ba 100644 --- a/packages/core-plugin-api/src/routing/SubRouteRef.ts +++ b/packages/core-plugin-api/src/routing/SubRouteRef.ts @@ -72,7 +72,7 @@ export type ParamNames = export type PathParams = { [name in ParamNames]: string }; /** - * Merges a param object type with with an optional params type into a params object. + * Merges a param object type with an optional params type into a params object. * @public */ export type MergeParams< diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 0e1d1a98af..52ed5caee8 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,45 @@ # @backstage/create-app +## 0.4.32 + +### Patch Changes + +- 58c2264325: Newly created Backstage repositories now use the stable version 6 of + `react-router`, just like the main repo does. Please let us know if you find any + issues with this. + + Migrating to the stable version of `react-router` is optional for the time + being. But if you want to do the same for your existing repository, please + follow [this + guide](https://backstage.io/docs/tutorials/react-router-stable-migration). + +- e05e0f021b: Update versions of packages used in the create-app template, to match those in the main repo +- 01dff06be4: Leverage cache mounts in Dockerfile during `yarn install ...` and `apt-get ...` commands to speed up repeated builds. +- 90616bcaa6: Add the new search pagination component to the search page template. +- 7c6306fc8a: Initializes a git repository when creating an app using @packages/create-app +- 52f25858a8: Added `*.session.sql` Visual Studio Code database functionality files to `.gitignore` in the default template. This is optional but potentially helpful if your developers use Visual Studio Code; you can add a line with that exact value to your own root `.gitignore` if you want the same. +- 6d00e80146: Updated the root `test` scripts to use `backstage-cli repo test`. + + To apply this change to an existing app, make the following change to the root `package.json`: + + ```diff + - "test": "backstage-cli test", + - "test:all": "lerna run test -- --coverage", + + "test": "backstage-cli repo test", + + "test:all": "backstage-cli repo test --coverage", + ``` + +- Updated dependencies + - @backstage/cli-common@0.1.10 + +## 0.4.32-next.2 + +### Patch Changes + +- 01dff06be4: Leverage cache mounts in Dockerfile during `yarn install ...` and `apt-get ...` commands to speed up repeated builds. +- Updated dependencies + - @backstage/cli-common@0.1.10 + ## 0.4.32-next.1 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 9633c890fa..a6a62fbbf0 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "A CLI that helps you create your own Backstage app", - "version": "0.4.32-next.1", + "version": "0.4.32", "publishConfig": { "access": "public" }, diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index 4d5ace5aa4..46cb786399 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -58,7 +58,7 @@ const AppRouter = app.getRouter(); const routes = ( - } /> + } /> } /> { + {({ results }) => ( diff --git a/packages/create-app/templates/default-app/packages/backend/Dockerfile b/packages/create-app/templates/default-app/packages/backend/Dockerfile index 682798b826..f0be3ac1d3 100644 --- a/packages/create-app/templates/default-app/packages/backend/Dockerfile +++ b/packages/create-app/templates/default-app/packages/backend/Dockerfile @@ -13,13 +13,18 @@ FROM node:16-bullseye-slim # Install sqlite3 dependencies. You can skip this if you don't use sqlite3 in the image, # in which case you should also move better-sqlite3 to "devDependencies" in package.json. -RUN apt-get update && \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update && \ apt-get install -y --no-install-recommends libsqlite3-dev python3 build-essential && \ - rm -rf /var/lib/apt/lists/* && \ yarn config set python /usr/bin/python3 # From here on we use the least-privileged `node` user to run the backend. USER node + +# This should create the app dir as `node`. +# If it is instead created as `root` then the `tar` command below will fail: `can't create directory 'packages/': Permission denied`. +# If this occurs, then ensure BuildKit is enabled (`DOCKER_BUILDKIT=1`) so the app dir is correctly created as `node`. WORKDIR /app # This switches many Node.js dependencies to production mode. @@ -31,7 +36,8 @@ ENV NODE_ENV production COPY --chown=node:node yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./ RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz -RUN yarn install --frozen-lockfile --production --network-timeout 300000 && rm -rf "$(yarn cache dir)" +RUN --mount=type=cache,target=/home/node/.cache/yarn,sharing=locked,uid=1000,gid=1000 \ + yarn install --frozen-lockfile --production --network-timeout 300000 # Then copy the rest of the backend bundle, along with any other files we might want. COPY --chown=node:node packages/backend/dist/bundle.tar.gz app-config*.yaml ./ diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 01c97a4c74..9513c71468 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/dev-utils +## 1.0.7 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-app-api@1.1.1 + - @backstage/test-utils@1.2.1 + - @backstage/app-defaults@1.0.7 + - @backstage/integration-react@1.1.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## 1.0.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/test-utils@1.2.1-next.2 + - @backstage/app-defaults@1.0.7-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-app-api@1.1.1-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + ## 1.0.7-next.1 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 60c877f736..225f4e9a78 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "1.0.7-next.1", + "version": "1.0.7", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/errors/CHANGELOG.md b/packages/errors/CHANGELOG.md index 26f45611fc..23978aeab8 100644 --- a/packages/errors/CHANGELOG.md +++ b/packages/errors/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/errors +## 1.1.2 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + +## 1.1.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + ## 1.1.2-next.1 ### Patch Changes diff --git a/packages/errors/package.json b/packages/errors/package.json index ad10bb7ed5..9abfeaaaaa 100644 --- a/packages/errors/package.json +++ b/packages/errors/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/errors", "description": "Common utilities for error handling within Backstage", - "version": "1.1.2-next.1", + "version": "1.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index 00439a9771..07d8fa516f 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/integration-react +## 1.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/integration@1.3.2 + - @backstage/theme@0.2.16 + +## 1.1.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/theme@0.2.16 + ## 1.1.5-next.1 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index cab328f88d..398ca58f36 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration-react", "description": "Frontend package for managing integrations towards external systems", - "version": "1.1.5-next.1", + "version": "1.1.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index 134dc34c11..244516e70e 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/integration +## 1.3.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## 1.3.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + ## 1.3.2-next.1 ### Patch Changes diff --git a/packages/integration/package.json b/packages/integration/package.json index 169bf56353..add2b07a93 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration", "description": "Helpers for managing integrations towards external systems", - "version": "1.3.2-next.1", + "version": "1.3.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index 49b59771c3..ffabaf3ffb 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,43 @@ # techdocs-cli-embedded-app +## 0.2.75 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog@1.6.0 + - @backstage/core-components@0.11.2 + - @backstage/core-app-api@1.1.1 + - @backstage/cli@0.20.0 + - @backstage/test-utils@1.2.1 + - @backstage/plugin-techdocs@1.3.3 + - @backstage/plugin-techdocs-react@1.0.5 + - @backstage/app-defaults@1.0.7 + - @backstage/integration-react@1.1.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/theme@0.2.16 + +## 0.2.75-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.20.0-next.2 + - @backstage/plugin-catalog@1.6.0-next.2 + - @backstage/test-utils@1.2.1-next.2 + - @backstage/app-defaults@1.0.7-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-app-api@1.1.1-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-techdocs@1.3.3-next.2 + - @backstage/plugin-techdocs-react@1.0.5-next.2 + ## 0.2.75-next.1 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 25eb9f2b27..2bcf099d80 100644 --- a/packages/techdocs-cli-embedded-app/package.json +++ b/packages/techdocs-cli-embedded-app/package.json @@ -1,6 +1,6 @@ { "name": "techdocs-cli-embedded-app", - "version": "0.2.75-next.1", + "version": "0.2.75", "private": true, "backstage": { "role": "frontend" diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index c04b3b8681..c5f85ab548 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,37 @@ # @techdocs/cli +## 1.2.2 + +### Patch Changes + +- 0b2a30dead: fixing techdocs-cli Docker client creation + + Docker client does not need to be created when --no-docker + option is provided. + + If you had DOCKER_CERT_PATH environment variable defined + the Docker client was looking for certificates + and breaking techdocs-cli generate command even with --no-docker + option. + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-techdocs-node@1.4.1 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3 + +## 1.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-techdocs-node@1.4.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3-next.2 + ## 1.2.2-next.1 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 3f00391865..1d721109bf 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,7 +1,7 @@ { "name": "@techdocs/cli", "description": "Utility CLI for managing TechDocs sites in Backstage.", - "version": "1.2.2-next.1", + "version": "1.2.2", "publishConfig": { "access": "public" }, diff --git a/packages/techdocs-cli/src/example-docs/docs/index.md b/packages/techdocs-cli/src/example-docs/docs/index.md index 074d3ccb3e..66e4c96b2f 100644 --- a/packages/techdocs-cli/src/example-docs/docs/index.md +++ b/packages/techdocs-cli/src/example-docs/docs/index.md @@ -73,20 +73,20 @@ const addThingToThing = (a, b) a + b; # MDX truly sane lists -- attributes +- `attributes` -- customer - - first_name - - test - - family_name - - email -- person - - first_name - - family_name - - birth_date -- subscription_id +- `customer` + - `first_name` + - `test` + - `family_name` + - `email` +- `person` + - `first_name` + - `family_name` + - `birth_date` +- `subscription_id` -- request +- `request` *[MOCDOC]: Mock Documentation diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index 28a4a92294..c891069ab5 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/test-utils +## 1.2.1 + +### Patch Changes + +- e05e0f021b: Align on the version of `@material-ui/icons` used, to `^4.9.1` like other packages in the main repo +- Updated dependencies + - @backstage/core-app-api@1.1.1 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + - @backstage/plugin-permission-react@0.4.6 + +## 1.2.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-permission-react@0.4.6-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-app-api@1.1.1-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + - @backstage/types@1.0.0 + ## 1.2.1-next.1 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index a7bcbab826..cf0bf2b225 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "1.2.1-next.1", + "version": "1.2.1", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/plugins/adr-backend/CHANGELOG.md b/plugins/adr-backend/CHANGELOG.md index a13f16e340..586caf36a1 100644 --- a/plugins/adr-backend/CHANGELOG.md +++ b/plugins/adr-backend/CHANGELOG.md @@ -1,5 +1,34 @@ # @backstage/plugin-adr-backend +## 0.2.2 + +### Patch Changes + +- 8006f8a602: In order to improve the debuggability of the search indexing process, messages logged during indexing are now tagged with a `documentType` whose value corresponds to the `type` being indexed. +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/catalog-client@1.1.1 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-adr-common@0.2.2 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + +## 0.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/plugin-adr-common@0.2.2-next.2 + ## 0.2.2-next.1 ### Patch Changes diff --git a/plugins/adr-backend/package.json b/plugins/adr-backend/package.json index ff232e6b69..30b623ace0 100644 --- a/plugins/adr-backend/package.json +++ b/plugins/adr-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr-backend", - "version": "0.2.2-next.1", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/adr-backend/src/search/DefaultAdrCollatorFactory.ts b/plugins/adr-backend/src/search/DefaultAdrCollatorFactory.ts index 21f0f1945f..78f6e0226e 100644 --- a/plugins/adr-backend/src/search/DefaultAdrCollatorFactory.ts +++ b/plugins/adr-backend/src/search/DefaultAdrCollatorFactory.ts @@ -113,7 +113,7 @@ export class DefaultAdrCollatorFactory implements DocumentCollatorFactory { this.catalogClient = options.catalogClient ?? new CatalogClient({ discoveryApi: options.discovery }); - this.logger = options.logger; + this.logger = options.logger.child({ documentType: this.type }); this.parser = options.parser ?? createMadrParser(); this.reader = options.reader; this.scmIntegrations = ScmIntegrations.fromConfig(options.config); diff --git a/plugins/adr-common/CHANGELOG.md b/plugins/adr-common/CHANGELOG.md index d1738d9920..be906e2ddc 100644 --- a/plugins/adr-common/CHANGELOG.md +++ b/plugins/adr-common/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-adr-common +## 0.2.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-search-common@1.1.0 + - @backstage/integration@1.3.2 + +## 0.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + ## 0.2.2-next.1 ### Patch Changes diff --git a/plugins/adr-common/package.json b/plugins/adr-common/package.json index 8358349631..e62be75b1c 100644 --- a/plugins/adr-common/package.json +++ b/plugins/adr-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-adr-common", "description": "Common functionalities for the adr plugin", - "version": "0.2.2-next.1", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/adr/CHANGELOG.md b/plugins/adr/CHANGELOG.md index 7ac3655e03..40a627a340 100644 --- a/plugins/adr/CHANGELOG.md +++ b/plugins/adr/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-adr +## 0.2.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/plugin-search-react@1.2.0 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-adr-common@0.2.2 + - @backstage/integration-react@1.1.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## 0.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-adr-common@0.2.2-next.2 + - @backstage/plugin-search-react@1.2.0-next.2 + ## 0.2.2-next.1 ### Patch Changes diff --git a/plugins/adr/package.json b/plugins/adr/package.json index 4eb9cf7b03..8e4835eb31 100644 --- a/plugins/adr/package.json +++ b/plugins/adr/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr", - "version": "0.2.2-next.1", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/airbrake-backend/CHANGELOG.md b/plugins/airbrake-backend/CHANGELOG.md index b61f76db54..0df7e1669c 100644 --- a/plugins/airbrake-backend/CHANGELOG.md +++ b/plugins/airbrake-backend/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-airbrake-backend +## 0.2.10 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/config@1.0.3 + +## 0.2.10-next.2 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/config@1.0.3-next.2 + ## 0.2.10-next.1 ### Patch Changes diff --git a/plugins/airbrake-backend/package.json b/plugins/airbrake-backend/package.json index 2817861740..d35d005137 100644 --- a/plugins/airbrake-backend/package.json +++ b/plugins/airbrake-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-airbrake-backend", - "version": "0.2.10-next.1", + "version": "0.2.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/airbrake/CHANGELOG.md b/plugins/airbrake/CHANGELOG.md index c466c6cb3a..68bc61ff92 100644 --- a/plugins/airbrake/CHANGELOG.md +++ b/plugins/airbrake/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-airbrake +## 0.3.10 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/test-utils@1.2.1 + - @backstage/dev-utils@1.0.7 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## 0.3.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/test-utils@1.2.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/dev-utils@1.0.7-next.2 + - @backstage/theme@0.2.16 + ## 0.3.10-next.1 ### Patch Changes diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index 28677b7948..dd65baa46d 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-airbrake", - "version": "0.3.10-next.1", + "version": "0.3.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/allure/CHANGELOG.md b/plugins/allure/CHANGELOG.md index ae170e29c7..5b6330f11e 100644 --- a/plugins/allure/CHANGELOG.md +++ b/plugins/allure/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-allure +## 0.1.26 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## 0.1.26-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + ## 0.1.26-next.1 ### Patch Changes diff --git a/plugins/allure/package.json b/plugins/allure/package.json index 1691a77ecf..ae0f87fb1e 100644 --- a/plugins/allure/package.json +++ b/plugins/allure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-allure", "description": "A Backstage plugin that integrates with Allure", - "version": "0.1.26-next.1", + "version": "0.1.26", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/analytics-module-ga/CHANGELOG.md b/plugins/analytics-module-ga/CHANGELOG.md index 13c445f246..1d16fce74d 100644 --- a/plugins/analytics-module-ga/CHANGELOG.md +++ b/plugins/analytics-module-ga/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-analytics-module-ga +## 0.1.21 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/theme@0.2.16 + +## 0.1.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + ## 0.1.21-next.1 ### Patch Changes diff --git a/plugins/analytics-module-ga/package.json b/plugins/analytics-module-ga/package.json index 87d6d4f0fc..dc3dd253f4 100644 --- a/plugins/analytics-module-ga/package.json +++ b/plugins/analytics-module-ga/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-ga", - "version": "0.1.21-next.1", + "version": "0.1.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/apache-airflow/CHANGELOG.md b/plugins/apache-airflow/CHANGELOG.md index 5cd28f0827..aa40bfc9b2 100644 --- a/plugins/apache-airflow/CHANGELOG.md +++ b/plugins/apache-airflow/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-apache-airflow +## 0.2.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + +## 0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + ## 0.2.3-next.1 ### Patch Changes diff --git a/plugins/apache-airflow/package.json b/plugins/apache-airflow/package.json index 5f9c1b4f9d..b66067cb6e 100644 --- a/plugins/apache-airflow/package.json +++ b/plugins/apache-airflow/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apache-airflow", - "version": "0.2.3-next.1", + "version": "0.2.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 432ff12f76..630587a60e 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-api-docs +## 0.8.10 + +### Patch Changes + +- 50c6e14aee: Updated dependency `@asyncapi/react-component` to `1.0.0-next.43`. +- 3d5bb521ee: Updated dependency `@asyncapi/react-component` to `1.0.0-next.42`. +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/plugin-catalog@1.6.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## 0.8.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.6.0-next.2 + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + ## 0.8.10-next.1 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index bd38fa9c65..1a5c8ae07c 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-api-docs", "description": "A Backstage plugin that helps represent API entities in the frontend", - "version": "0.8.10-next.1", + "version": "0.8.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/apollo-explorer/CHANGELOG.md b/plugins/apollo-explorer/CHANGELOG.md index a1c6c6c8df..3a56b728fe 100644 --- a/plugins/apollo-explorer/CHANGELOG.md +++ b/plugins/apollo-explorer/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-apollo-explorer +## 0.1.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## 0.1.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + ## 0.1.3-next.1 ### Patch Changes diff --git a/plugins/apollo-explorer/package.json b/plugins/apollo-explorer/package.json index a5e598ede1..3e2f958fe7 100644 --- a/plugins/apollo-explorer/package.json +++ b/plugins/apollo-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apollo-explorer", - "version": "0.1.3-next.1", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index b1b112a030..ba4a4b9ea4 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-app-backend +## 0.3.37 + +### Patch Changes + +- 11c9e0ad33: Added alpha plugin implementation for the new backend system. Available at `@backstage/plugin-app-backend/alpha`. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/backend-plugin-api@0.1.3 + - @backstage/config@1.0.3 + - @backstage/config-loader@1.1.5 + - @backstage/types@1.0.0 + +## 0.3.37-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/config-loader@1.1.5-next.2 + - @backstage/types@1.0.0 + ## 0.3.37-next.1 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index db100ea12b..d3b14e6afd 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-app-backend", "description": "A Backstage backend plugin that serves the Backstage frontend app", - "version": "0.3.37-next.1", + "version": "0.3.37", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index afa2d700fd..11c6cedb8e 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/plugin-auth-backend +## 0.17.0 + +### Minor Changes + +- e2dc42e9f0: Google OAuth refresh tokens will now be revoked on logout by calling Google's API +- 5fa831ce55: CookieConfigurer can optionally return the `SameSite` cookie attribute. + CookieConfigurer now requires an additional argument `appOrigin` - the origin URL of the app - which is used to calculate the `SameSite` attribute. + defaultCookieConfigurer returns the `SameSite` attribute which defaults to `Lax`. In cases where an auth-backend is running on a different domain than the App, `SameSite=None` is used - but only for secure contexts. This is so that cookies can be included in third-party requests. + + OAuthAdapterOptions has been modified to require additional arguments, `baseUrl`, and `cookieConfigurer`. + OAuthAdapter now resolves cookie configuration using its supplied CookieConfigurer for each request to make sure that the proper attributes always are set. + +### Patch Changes + +- b5c126010c: Auth0 provider now supports optional `connection` and `connectionScope` parameters to configure social identity providers. +- 8c6ec175bf: Fix GitLab provider setup so that it supports GitLab installations with a path in the URL. +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/catalog-client@1.1.1 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## 0.17.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + ## 0.17.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index be02a90a71..23482f8eb7 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend", "description": "A Backstage backend plugin that handles authentication", - "version": "0.17.0-next.1", + "version": "0.17.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index 147861383f..ab1229ea69 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-auth-node +## 0.2.6 + +### Patch Changes + +- f3a3fefb96: Ensure `getIdentity` throws an `AuthenticationError` instead of a `NotAllowed` error when authentication fails +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## 0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + ## 0.2.6-next.1 ### Patch Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index 7c0c3b51e2..f657a2df01 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.2.6-next.1", + "version": "0.2.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-node/src/DefaultIdentityClient.ts b/plugins/auth-node/src/DefaultIdentityClient.ts index a7946c77ff..499fc62929 100644 --- a/plugins/auth-node/src/DefaultIdentityClient.ts +++ b/plugins/auth-node/src/DefaultIdentityClient.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { PluginEndpointDiscovery } from '@backstage/backend-common'; -import { AuthenticationError, NotAllowedError } from '@backstage/errors'; +import { AuthenticationError } from '@backstage/errors'; import { createRemoteJWKSet, decodeJwt, @@ -86,7 +86,7 @@ export class DefaultIdentityClient implements IdentityApi { getBearerTokenFromAuthorizationHeader(request.headers.authorization), ); } catch (e) { - throw new NotAllowedError(e.message); + throw new AuthenticationError(e.message); } } diff --git a/plugins/azure-devops-backend/CHANGELOG.md b/plugins/azure-devops-backend/CHANGELOG.md index 00cc426c0e..c7721c604d 100644 --- a/plugins/azure-devops-backend/CHANGELOG.md +++ b/plugins/azure-devops-backend/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-azure-devops-backend +## 0.3.16 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/config@1.0.3 + - @backstage/plugin-azure-devops-common@0.3.0 + +## 0.3.16-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/plugin-azure-devops-common@0.3.0 + ## 0.3.16-next.1 ### Patch Changes diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index 27be6291ed..e429240ab3 100644 --- a/plugins/azure-devops-backend/package.json +++ b/plugins/azure-devops-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-backend", - "version": "0.3.16-next.1", + "version": "0.3.16", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-devops/CHANGELOG.md b/plugins/azure-devops/CHANGELOG.md index 4919b1226d..7c5c8983bf 100644 --- a/plugins/azure-devops/CHANGELOG.md +++ b/plugins/azure-devops/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-azure-devops +## 0.2.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-azure-devops-common@0.3.0 + +## 0.2.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-azure-devops-common@0.3.0 + ## 0.2.1-next.1 ### Patch Changes diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index 14f2c29310..3e1a69b3dc 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops", - "version": "0.2.1-next.1", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-sites-backend/.eslintrc.js b/plugins/azure-sites-backend/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/azure-sites-backend/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/azure-sites-backend/README.md b/plugins/azure-sites-backend/README.md new file mode 100644 index 0000000000..5f19267499 --- /dev/null +++ b/plugins/azure-sites-backend/README.md @@ -0,0 +1,85 @@ +# Azure Sites Backend + +Simple plugin that proxies requests to the Azure Portal API through Azure SDK JavaScript libraries. + +_Inspired by [roadie.io AWS Lamda plugin](https://roadie.io/backstage/plugins/aws-lambda/)_ + +## Setup + +The following sections will help you get the Azure Sites Backend plugin setup and running. + +### Configuration + +The Azure plugin requires the following YAML to be added to your app-config.yaml: + +```yaml +azureSites: + domain: + tenantId: + clientId: + clientSecret: + allowedSubscriptions: + - id: +``` + +Configuration Details: + +- `domain` can be found by visiting the [Directories + Subscriptions settings page](https://portal.azure.com/#settings/directory). Alternatively you can inspect the [Azure home](https://portal.azure.com/#home) URL - `https://portal.azure.com/#@/`. +- `tenantId` can be found by visiting [Azure Directory Overview page](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade). +- (Optional) `clientId` and `clientSecret` can be the same values you used for [Azure DevOps Backend](https://github.com/backstage/backstage/tree/master/plugins/azure-devops-backend) or [Azure Integration](https://backstage.io/docs/integrations/azure/org#app-registration) as long as this App Registration has permissions to read your function apps. +- (Optional) `allowedSubscriptions` is an array of `id` that will be used to iterate over and look for the specified functions' app. `id` can be found the [Subscriptions page](https://portal.azure.com/#view/Microsoft_Azure_Billing/SubscriptionsBlade). + +### Integrating + +Here's how to get the backend plugin up and running: + +1. First we need to add the `@backstage/plugin-azure-sites-backend` package to your backend: + + ```sh + # From the Backstage root directory + yarn add --cwd packages/backend @backstage/plugin-azure-sites-backend + ``` + +2. Then we will create a new file named `packages/backend/src/plugins/azure.ts`, and add the following to it: + + ```ts + import { + createRouter, + AzureSitesApi, + } from '@backstage/plugin-azure-sites-backend'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + return await createRouter({ + logger: env.logger, + azureSitesApi: AzureSitesApi.fromConfig(env.config), + }); + } + ``` + +3. Next we wire this into the overall backend router, edit `packages/backend/src/index.ts`: + + ```ts + import azure from './plugins/azure'; + + // Removed for clairty... + + async function main() { + // ... + // Add this line under the other lines that follow the useHotMemoize pattern + const azureSitesEnv = useHotMemoize(module, () => + createEnv('azure-sites'), + ); + + // ... + // Insert this line under the other lines that add their routers to apiRouter in the same way + apiRouter.use('/azure-sites', await azure(azureSitesEnv)); + } + ``` + +4. Now run `yarn start-backend` from the repo root. + +5. Finally, open `http://localhost:7007/api/azure/health` in a browser, it should return `{"status":"ok"}`. diff --git a/plugins/azure-sites-backend/api-report.md b/plugins/azure-sites-backend/api-report.md new file mode 100644 index 0000000000..751ff3f033 --- /dev/null +++ b/plugins/azure-sites-backend/api-report.md @@ -0,0 +1,61 @@ +## API Report File for "@backstage/plugin-azure-sites-backend" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { AzureSiteListRequest } from '@backstage/plugin-azure-sites-common'; +import { AzureSiteListResponse } from '@backstage/plugin-azure-sites-common'; +import { AzureSiteStartStopRequest } from '@backstage/plugin-azure-sites-common'; +import { Config } from '@backstage/config'; +import express from 'express'; +import { Logger } from 'winston'; + +// @public (undocumented) +export class AzureSitesApi { + constructor(config: AzureSitesConfig); + // (undocumented) + static fromConfig(config: Config): AzureSitesApi; + // (undocumented) + list(request: AzureSiteListRequest): Promise; + // (undocumented) + start(request: AzureSiteStartStopRequest): Promise; + // (undocumented) + stop(request: AzureSiteStartStopRequest): Promise; +} + +// @public (undocumented) +export class AzureSitesConfig { + constructor( + domain: string, + tenantId: string, + subscriptions?: string[] | undefined, + clientId?: string | undefined, + clientSecret?: string | undefined, + ); + // (undocumented) + readonly clientId?: string | undefined; + // (undocumented) + readonly clientSecret?: string | undefined; + // (undocumented) + readonly domain: string; + // (undocumented) + static fromConfig(config: Config): AzureSitesConfig; + // (undocumented) + readonly subscriptions?: string[] | undefined; + // (undocumented) + readonly tenantId: string; +} + +// @public (undocumented) +export function createRouter(options: RouterOptions): Promise; + +// @public (undocumented) +export interface RouterOptions { + // (undocumented) + azureSitesApi: AzureSitesApi; + // (undocumented) + logger: Logger; +} + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/azure-sites-backend/package.json b/plugins/azure-sites-backend/package.json new file mode 100644 index 0000000000..6930f36587 --- /dev/null +++ b/plugins/azure-sites-backend/package.json @@ -0,0 +1,56 @@ +{ + "name": "@backstage/plugin-azure-sites-backend", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/azure-sites-backend" + }, + "keywords": [ + "backstage", + "azure" + ], + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@azure/arm-appservice": "^13.0.1", + "@azure/arm-resourcegraph": "^4.2.1", + "@azure/identity": "^2.1.0", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/plugin-azure-sites-common": "workspace:^", + "@types/express": "^4.17.6", + "express": "^4.17.1", + "express-promise-router": "^4.1.0", + "node-fetch": "^2.6.7", + "winston": "^3.2.1", + "yn": "^4.0.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@types/supertest": "^2.0.8", + "msw": "^0.47.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/azure-sites-backend/schema.d.ts b/plugins/azure-sites-backend/schema.d.ts new file mode 100644 index 0000000000..19a12d11a1 --- /dev/null +++ b/plugins/azure-sites-backend/schema.d.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2022 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 interface Config { + azureSites: { + /** @visibility backend */ + domain: string; + /** @visibility backend */ + tenantId: string; + /** @visibility backend */ + subscriptions?: [ + { + id: string; + }, + ]; + /** @visibility backend */ + clientId?: string; + /** @visibility secret */ + clientSecret?: string; + }; +} diff --git a/plugins/azure-sites-backend/src/api/AzureSitesApi.ts b/plugins/azure-sites-backend/src/api/AzureSitesApi.ts new file mode 100644 index 0000000000..73e92ea6a0 --- /dev/null +++ b/plugins/azure-sites-backend/src/api/AzureSitesApi.ts @@ -0,0 +1,98 @@ +/* + * Copyright 2022 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 { + DefaultAzureCredential, + ClientSecretCredential, +} from '@azure/identity'; +import { ResourceGraphClient } from '@azure/arm-resourcegraph'; +import { WebSiteManagementClient } from '@azure/arm-appservice'; +import { + AzureSite, + AzureSiteListRequest, + AzureSiteListResponse, + AzureSiteStartStopRequest, +} from '@backstage/plugin-azure-sites-common'; +import { AzureSitesConfig } from '../config'; + +/** @public */ +export class AzureSitesApi { + private readonly baseHref = (domain: string) => + `https://portal.azure.com/#@${domain}/resource`; + private readonly client: ResourceGraphClient; + + constructor(private readonly config: AzureSitesConfig) { + const creds = this.getCredentials(config); + + this.client = new ResourceGraphClient(creds); + } + + private getCredentials(config: AzureSitesConfig) { + return config.clientId && config.clientSecret + ? new ClientSecretCredential( + config.tenantId, + config.clientId, + config.clientSecret, + ) + : new DefaultAzureCredential({ tenantId: config.tenantId }); + } + + static fromConfig(config: Config): AzureSitesApi { + return new AzureSitesApi(AzureSitesConfig.fromConfig(config)); + } + + async start(request: AzureSiteStartStopRequest): Promise { + const client = new WebSiteManagementClient( + this.getCredentials(this.config), + request.subscription, + ); + await client.webApps.start(request.resourceGroup, request.name); + } + + async stop(request: AzureSiteStartStopRequest): Promise { + const client = new WebSiteManagementClient( + this.getCredentials(this.config), + request.subscription, + ); + await client.webApps.stop(request.resourceGroup, request.name); + } + + async list(request: AzureSiteListRequest): Promise { + const items: AzureSite[] = []; + const result = await this.client.resources({ + query: `resources | where type == 'microsoft.web/sites' | where name contains '${request.name}'`, + subscriptions: this.config.subscriptions, + }); + for (const v of result.data) { + items.push({ + href: `${this.baseHref(this.config.domain)}${v.id!}`, + logstreamHref: `${this.baseHref(this.config.domain)}${v.id!}/logStream`, + name: v.name!, + kind: v.kind!, + resourceGroup: v.resourceGroup!, + subscription: v.id!.match(/\w{8}\-\w{4}\-\w{4}\-\w{4}\-\w{12}/)[0], + location: v.location!, + lastModifiedDate: v.properties.lastModifiedTimeUtc!, + usageState: v.properties.usageState!, + state: v.properties.state!, + containerSize: v.properties.containerSize!, + tags: v.properties.tags!, + }); + } + return { items: items }; + } +} diff --git a/packages/core-components/src/layout/HomepageTimer/index.ts b/plugins/azure-sites-backend/src/api/index.ts similarity index 86% rename from packages/core-components/src/layout/HomepageTimer/index.ts rename to plugins/azure-sites-backend/src/api/index.ts index bc004fc757..187dc1314b 100644 --- a/packages/core-components/src/layout/HomepageTimer/index.ts +++ b/plugins/azure-sites-backend/src/api/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * Copyright 2022 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. @@ -14,4 +14,4 @@ * limitations under the License. */ -export { HomepageTimer } from './HomepageTimer'; +export { AzureSitesApi } from './AzureSitesApi'; diff --git a/plugins/azure-sites-backend/src/config.ts b/plugins/azure-sites-backend/src/config.ts new file mode 100644 index 0000000000..3475175cb5 --- /dev/null +++ b/plugins/azure-sites-backend/src/config.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2022 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'; + +/** @public */ +export class AzureSitesConfig { + constructor( + public readonly domain: string, + public readonly tenantId: string, + public readonly subscriptions?: string[], + public readonly clientId?: string, + public readonly clientSecret?: string, + ) {} + + static fromConfig(config: Config): AzureSitesConfig { + const azConfig = config.getConfig('azureSites'); + + return new AzureSitesConfig( + azConfig.getString('domain'), + azConfig.getString('tenantId'), + azConfig + .getOptionalConfigArray('subscriptions') + ?.map(as => as.getString('id')), + azConfig.getOptionalString('clientId'), + azConfig.getOptionalString('clientSecret'), + ); + } +} diff --git a/plugins/azure-sites-backend/src/index.ts b/plugins/azure-sites-backend/src/index.ts new file mode 100644 index 0000000000..97f94e4470 --- /dev/null +++ b/plugins/azure-sites-backend/src/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2022 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 * from './service/router'; +export * from './api'; +export * from './config'; diff --git a/plugins/azure-sites-backend/src/run.ts b/plugins/azure-sites-backend/src/run.ts new file mode 100644 index 0000000000..d945aa13f0 --- /dev/null +++ b/plugins/azure-sites-backend/src/run.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2022 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 { getRootLogger } from '@backstage/backend-common'; +import yn from 'yn'; +import { startStandaloneServer } from './service/standaloneServer'; + +const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7007; +const enableCors = yn(process.env.PLUGIN_CORS, { default: false }); +const logger = getRootLogger(); + +startStandaloneServer({ port, enableCors, logger }).catch(err => { + logger.error(err); + process.exit(1); +}); + +process.on('SIGINT', () => { + logger.info('CTRL+C pressed; exiting.'); + process.exit(0); +}); diff --git a/plugins/azure-sites-backend/src/service/router.ts b/plugins/azure-sites-backend/src/service/router.ts new file mode 100644 index 0000000000..bea9a15396 --- /dev/null +++ b/plugins/azure-sites-backend/src/service/router.ts @@ -0,0 +1,81 @@ +/* + * Copyright 2022 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 { errorHandler } from '@backstage/backend-common'; +import express from 'express'; +import Router from 'express-promise-router'; +import { Logger } from 'winston'; + +import { AzureSitesApi } from '../api'; + +/** @public */ +export interface RouterOptions { + logger: Logger; + azureSitesApi: AzureSitesApi; +} + +/** @public */ +export async function createRouter( + options: RouterOptions, +): Promise { + const { logger, azureSitesApi } = options; + + const router = Router(); + router.use(express.json()); + + router.get('/health', (_, response) => { + logger.info('PONG!'); + response.send({ status: 'ok' }); + }); + router.get('/list/:name', async (request, response) => { + const { name } = request.params; + response.json( + await azureSitesApi.list({ + name: name, + }), + ); + }); + router.post( + '/:subscription/:resourceGroup/:name/start', + async (request, response) => { + const { subscription, resourceGroup, name } = request.params; + console.log('starting...'); + response.json( + await azureSitesApi.start({ + subscription, + resourceGroup, + name, + }), + ); + }, + ); + router.post( + '/:subscription/:resourceGroup/:name/stop', + async (request, response) => { + const { subscription, resourceGroup, name } = request.params; + console.log('stopping...'); + response.json( + await azureSitesApi.stop({ + subscription, + resourceGroup, + name, + }), + ); + }, + ); + router.use(errorHandler()); + return router; +} diff --git a/plugins/azure-sites-backend/src/service/standaloneServer.ts b/plugins/azure-sites-backend/src/service/standaloneServer.ts new file mode 100644 index 0000000000..1998bee023 --- /dev/null +++ b/plugins/azure-sites-backend/src/service/standaloneServer.ts @@ -0,0 +1,56 @@ +/* + * Copyright 2022 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 { + createServiceBuilder, + loadBackendConfig, +} from '@backstage/backend-common'; +import { Server } from 'http'; +import { Logger } from 'winston'; +import { AzureSitesApi } from '../api'; +import { createRouter } from './router'; + +export interface ServerOptions { + port: number; + enableCors: boolean; + logger: Logger; +} + +export async function startStandaloneServer( + options: ServerOptions, +): Promise { + const logger = options.logger.child({ service: 'azure-backend' }); + const config = await loadBackendConfig({ logger, argv: process.argv }); + logger.debug('Starting application server...'); + const router = await createRouter({ + logger, + azureSitesApi: AzureSitesApi.fromConfig(config), + }); + + let service = createServiceBuilder(module) + .setPort(options.port) + .addRouter('/azure-backend', router); + if (options.enableCors) { + service = service.enableCors({ origin: 'http://localhost:3000' }); + } + + return await service.start().catch(err => { + logger.error(err); + process.exit(1); + }); +} + +module.hot?.accept(); diff --git a/plugins/azure-sites-backend/src/setupTests.ts b/plugins/azure-sites-backend/src/setupTests.ts new file mode 100644 index 0000000000..813cdeaae3 --- /dev/null +++ b/plugins/azure-sites-backend/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 {}; diff --git a/plugins/azure-sites-common/.eslintrc.js b/plugins/azure-sites-common/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/azure-sites-common/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/azure-sites-common/api-report.md b/plugins/azure-sites-common/api-report.md new file mode 100644 index 0000000000..64b4275085 --- /dev/null +++ b/plugins/azure-sites-common/api-report.md @@ -0,0 +1,40 @@ +## API Report File for "@backstage/plugin-azure-sites-common" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +// @public (undocumented) +export type AzureSite = { + href: string; + logstreamHref: string; + name: string; + kind: string; + location: string; + resourceGroup: string; + subscription: string; + state: string; + usageState: string; + containerSize: number; + lastModifiedDate: string; + tags: {}; +}; + +// @public (undocumented) +export type AzureSiteListRequest = { + name: string; +}; + +// @public (undocumented) +export type AzureSiteListResponse = { + items: AzureSite[]; +}; + +// @public (undocumented) +export type AzureSiteStartStopRequest = { + subscription: string; + resourceGroup: string; + name: string; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/azure-sites-common/package.json b/plugins/azure-sites-common/package.json new file mode 100644 index 0000000000..eb0093bf65 --- /dev/null +++ b/plugins/azure-sites-common/package.json @@ -0,0 +1,40 @@ +{ + "name": "@backstage/plugin-azure-sites-common", + "description": "Common functionalities for the azure plugin", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "common-library" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/azure-sites-common" + }, + "keywords": [ + "backstage" + ], + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "devDependencies": { + "@backstage/cli": "workspace:^" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/azure-sites-common/src/index.ts b/plugins/azure-sites-common/src/index.ts new file mode 100644 index 0000000000..db229eae34 --- /dev/null +++ b/plugins/azure-sites-common/src/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 * from './types'; diff --git a/plugins/azure-sites-common/src/types.ts b/plugins/azure-sites-common/src/types.ts new file mode 100644 index 0000000000..7c8be7ce24 --- /dev/null +++ b/plugins/azure-sites-common/src/types.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2022 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. + */ + +/** @public */ +export type AzureSite = { + href: string; + logstreamHref: string; + name: string; + kind: string; + location: string; + resourceGroup: string; + subscription: string; + state: string; + usageState: string; + containerSize: number; + lastModifiedDate: string; + tags: {}; +}; + +/** @public */ +export type AzureSiteListRequest = { + name: string; +}; + +/** @public */ +export type AzureSiteListResponse = { + items: AzureSite[]; +}; + +/** @public */ +export type AzureSiteStartStopRequest = { + subscription: string; + resourceGroup: string; + name: string; +}; diff --git a/plugins/azure-sites/.eslintrc.js b/plugins/azure-sites/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/azure-sites/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/azure-sites/README.md b/plugins/azure-sites/README.md new file mode 100644 index 0000000000..f240dab140 --- /dev/null +++ b/plugins/azure-sites/README.md @@ -0,0 +1,70 @@ +# Azure Sites Plugin + +![preview of Azure table](docs/functions-table.png) + +_Inspired by [roadie.io AWS Lamda plugin](https://roadie.io/backstage/plugins/aws-lambda/)_ + +## Features + +- Azure overview table + +## Plugin Setup + +The following sections will help you get the Azure plugin setup and running + +### Azure Sites Backend + +You need to set up the Azure Sites Backend plugin before you move forward with any of these steps if you haven't already. + +### Entity Annotation + +To be able to use the Azure Sites plugin you need to add the following annotation to any entities you want to use it with: + +```yaml +azure.com/microsoft-web-sites: +``` + +`` supports case-insensitive exact / partial value. + +Example of Partial Matching: + +Let's say you have a number of functions apps, spread out over different regions (and possibly different subscriptions), and they follow a naming convention: + +``` +func-testapp-eu +func-testapp-ca +func-testapp-us +``` + +The annotation you will use to have the three functions' app appear in the overview table would look like this: + +```yaml +azure.com/microsoft-web-sites: func-testapp +``` + +### Install the component + +1. Install the plugin in the `packages/app` directory + +```sh +yarn add @backstage/plugin-azure-sites +``` + +2. Add widget component to your Backstage instance: + +```ts +// In packages/app/src/components/catalog/EntityPage.tsx +import { EntityAzureSitesOverviewWidget, isAzureWebSiteNameAvailable } from '@backstage/plugin-azure-sites'; + +... + +const serviceEntityPage = ( + + //... + Boolean(isAzureWebSiteNameAvailable(e))} path="/azure" title="Azure"> + + + //... + +); +``` diff --git a/plugins/azure-sites/api-report.md b/plugins/azure-sites/api-report.md new file mode 100644 index 0000000000..38ca6a8fe2 --- /dev/null +++ b/plugins/azure-sites/api-report.md @@ -0,0 +1,63 @@ +## API Report File for "@backstage/plugin-azure-sites" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { ApiRef } from '@backstage/core-plugin-api'; +import { AzureSiteListRequest } from '@backstage/plugin-azure-sites-common'; +import { AzureSiteListResponse } from '@backstage/plugin-azure-sites-common'; +import { AzureSiteStartStopRequest } from '@backstage/plugin-azure-sites-common'; +import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { DiscoveryApi } from '@backstage/core-plugin-api'; +import { Entity } from '@backstage/catalog-model'; +import { IdentityApi } from '@backstage/core-plugin-api'; +import { RouteRef } from '@backstage/core-plugin-api'; + +// @public (undocumented) +export const azureSiteApiRef: ApiRef; + +// @public (undocumented) +export type AzureSitesApi = { + list: (request: AzureSiteListRequest) => Promise; + start: (request: AzureSiteStartStopRequest) => Promise; + stop: (request: AzureSiteStartStopRequest) => Promise; +}; + +// @public (undocumented) +export class AzureSitesApiBackendClient implements AzureSitesApi { + constructor(options: { + discoveryApi: DiscoveryApi; + identityApi: IdentityApi; + }); + // (undocumented) + list(request: AzureSiteListRequest): Promise; + // (undocumented) + start(request: AzureSiteStartStopRequest): Promise; + // (undocumented) + stop(request: AzureSiteStartStopRequest): Promise; +} + +// @public (undocumented) +export const AzureSitesOverviewWidget: () => JSX.Element; + +// @public (undocumented) +export const azureSitesPlugin: BackstagePlugin< + { + entityContent: RouteRef; + }, + {}, + {} +>; + +// @public (undocumented) +export const EntityAzureSitesOverviewWidget: () => JSX.Element; + +// @public (undocumented) +export const isAzureWebSiteNameAvailable: ( + entity: Entity, +) => string | undefined; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/azure-sites/dev/index.tsx b/plugins/azure-sites/dev/index.tsx new file mode 100644 index 0000000000..02f8ed99cd --- /dev/null +++ b/plugins/azure-sites/dev/index.tsx @@ -0,0 +1,20 @@ +/* + * Copyright 2022 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 { createDevApp } from '@backstage/dev-utils'; +import { azureSitesPlugin } from '../src/plugin'; + +createDevApp().registerPlugin(azureSitesPlugin).render(); diff --git a/plugins/azure-sites/docs/functions-table.png b/plugins/azure-sites/docs/functions-table.png new file mode 100644 index 0000000000..d8603e8fa7 Binary files /dev/null and b/plugins/azure-sites/docs/functions-table.png differ diff --git a/plugins/azure-sites/package.json b/plugins/azure-sites/package.json new file mode 100644 index 0000000000..2ffe0225e6 --- /dev/null +++ b/plugins/azure-sites/package.json @@ -0,0 +1,65 @@ +{ + "name": "@backstage/plugin-azure-sites", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "frontend-plugin" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/azure-sites" + }, + "keywords": [ + "backstage", + "azure" + ], + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-azure-sites-common": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", + "@material-ui/core": "^4.12.2", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.57", + "luxon": "^3.0.0", + "react-use": "^17.2.4" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^12.1.3", + "@testing-library/user-event": "^14.0.0", + "@types/node": "^16.11.26", + "cross-fetch": "^3.1.5", + "msw": "^0.47.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/azure-sites/src/api/AzureSitesApi.ts b/plugins/azure-sites/src/api/AzureSitesApi.ts new file mode 100644 index 0000000000..87a3b46a8d --- /dev/null +++ b/plugins/azure-sites/src/api/AzureSitesApi.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2022 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 { createApiRef } from '@backstage/core-plugin-api'; +import { + AzureSiteListRequest, + AzureSiteListResponse, + AzureSiteStartStopRequest, +} from '@backstage/plugin-azure-sites-common'; + +/** @public */ +export const azureSiteApiRef = createApiRef({ + id: 'plugin.azure-sites.service', +}); + +/** @public */ +export type AzureSitesApi = { + list: (request: AzureSiteListRequest) => Promise; + start: (request: AzureSiteStartStopRequest) => Promise; + stop: (request: AzureSiteStartStopRequest) => Promise; +}; diff --git a/plugins/azure-sites/src/api/AzureSitesApiBackendClient.ts b/plugins/azure-sites/src/api/AzureSitesApiBackendClient.ts new file mode 100644 index 0000000000..fc622e786b --- /dev/null +++ b/plugins/azure-sites/src/api/AzureSitesApiBackendClient.ts @@ -0,0 +1,78 @@ +/* + * Copyright 2022 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 { AzureSitesApi } from './AzureSitesApi'; +import { + AzureSiteListRequest, + AzureSiteListResponse, + AzureSiteStartStopRequest, +} from '@backstage/plugin-azure-sites-common'; +import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; + +/** @public */ +export class AzureSitesApiBackendClient implements AzureSitesApi { + private readonly identityApi: IdentityApi; + private readonly discoveryApi: DiscoveryApi; + constructor(options: { + discoveryApi: DiscoveryApi; + identityApi: IdentityApi; + }) { + this.discoveryApi = options.discoveryApi; + this.identityApi = options.identityApi; + } + + async stop(request: AzureSiteStartStopRequest): Promise { + const url = `${await this.discoveryApi.getBaseUrl('azure-functions')}/${ + request.subscription + }/${request.resourceGroup}/${request.name}/stop`; + const { token: accessToken } = await this.identityApi.getCredentials(); + await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...(accessToken && { Authorization: `Bearer ${accessToken}` }), + }, + }); + } + async start(request: AzureSiteStartStopRequest): Promise { + const url = `${await this.discoveryApi.getBaseUrl('azure-functions')}/${ + request.subscription + }/${request.resourceGroup}/${request.name}/start`; + const { token: accessToken } = await this.identityApi.getCredentials(); + await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...(accessToken && { Authorization: `Bearer ${accessToken}` }), + }, + }); + } + + async list(request: AzureSiteListRequest): Promise { + const url = `${await this.discoveryApi.getBaseUrl('azure-sites')}/list/${ + request.name + }`; + const { token: accessToken } = await this.identityApi.getCredentials(); + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + ...(accessToken && { Authorization: `Bearer ${accessToken}` }), + }, + }); + return await response.json(); + } +} diff --git a/plugins/azure-sites/src/api/index.ts b/plugins/azure-sites/src/api/index.ts new file mode 100644 index 0000000000..2b6cc64221 --- /dev/null +++ b/plugins/azure-sites/src/api/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2022 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 * from './AzureSitesApi'; +export * from './AzureSitesApiBackendClient'; diff --git a/plugins/azure-sites/src/components/AzureSitesOverviewComponent/AzureSitesOverview.tsx b/plugins/azure-sites/src/components/AzureSitesOverviewComponent/AzureSitesOverview.tsx new file mode 100644 index 0000000000..755a42f5da --- /dev/null +++ b/plugins/azure-sites/src/components/AzureSitesOverviewComponent/AzureSitesOverview.tsx @@ -0,0 +1,76 @@ +/* + * Copyright 2022 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 React from 'react'; +import { Entity } from '@backstage/catalog-model'; +import { useSites } from '../../hooks/useSites'; +import { + AZURE_WEB_SITE_NAME_ANNOTATION, + useServiceEntityAnnotations, +} from '../../hooks/useServiceEntityAnnotations'; +import { + ErrorBoundary, + MissingAnnotationEmptyState, + ResponseErrorPanel, +} from '@backstage/core-components'; +import { useEntity } from '@backstage/plugin-catalog-react'; +import { AzureSitesOverviewTable } from '../AzureSitesOverviewTableComponent/AzureSitesOverviewTable'; + +/** @public */ +export const isAzureWebSiteNameAvailable = (entity: Entity) => + entity?.metadata.annotations?.[AZURE_WEB_SITE_NAME_ANNOTATION]; + +const AzureSitesOverview = ({ entity }: { entity: Entity }) => { + const { webSiteName } = useServiceEntityAnnotations(entity); + + const [sites] = useSites({ + name: webSiteName, + }); + + if (sites.error) { + return ( +
+ +
+ ); + } + + return ( + + ); +}; + +/** @public */ +export const AzureSitesOverviewWidget = () => { + const { entity } = useEntity(); + + if (!isAzureWebSiteNameAvailable(entity)) { + return ( + + ); + } + + return ( + + + + ); +}; diff --git a/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx b/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx new file mode 100644 index 0000000000..23476b5c3e --- /dev/null +++ b/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx @@ -0,0 +1,86 @@ +/* + * Copyright 2022 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 React from 'react'; +import { render } from '@testing-library/react'; +import { + AnyApiRef, + configApiRef, + errorApiRef, + identityApiRef, +} from '@backstage/core-plugin-api'; +import { rest } from 'msw'; +import { + setupRequestMockHandlers, + TestApiProvider, +} from '@backstage/test-utils'; +import { setupServer } from 'msw/node'; +import { DateTime } from 'luxon'; +import { siteMock } from '../../mocks/mocks'; +import { azureSiteApiRef } from '../..'; +import { AzureSitesOverviewTable } from './AzureSitesOverviewTable'; + +const errorApiMock = { post: jest.fn(), error$: jest.fn() }; +const identityApiMock = (getCredentials: any) => ({ + signOut: jest.fn(), + getProfileInfo: jest.fn(), + getBackstageIdentity: jest.fn(), + getCredentials, +}); +const azureSitesApiMock = {}; + +const config = { + getString: (_: string) => 'https://test-url', +}; + +const apis: [AnyApiRef, Partial][] = [ + [errorApiRef, errorApiMock], + [configApiRef, config], + [azureSiteApiRef, azureSitesApiMock], + [identityApiRef, identityApiMock], +]; + +describe('AzureSitesOverviewWidget', () => { + const worker = setupServer(); + setupRequestMockHandlers(worker); + + beforeEach(() => { + worker.use( + rest.get('/list/func-mock', (_, res, ctx) => { + res(ctx.json(siteMock)); + }), + ); + }); + + it('should display an overview table with the data from the requests', async () => { + const rendered = render( + + + , + ); + + expect(await rendered.findByText(siteMock.name)).toBeInTheDocument(); + expect(await rendered.findByText(siteMock.location)).toBeInTheDocument(); + expect(await rendered.findByText(siteMock.state)).toBeInTheDocument(); + expect( + await rendered.findByText( + DateTime.fromISO(siteMock.lastModifiedDate).toLocaleString( + DateTime.DATETIME_MED, + ), + ), + ).toBeInTheDocument(); + }); +}); diff --git a/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.tsx b/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.tsx new file mode 100644 index 0000000000..3f9895a226 --- /dev/null +++ b/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.tsx @@ -0,0 +1,271 @@ +/* + * Copyright 2022 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 React, { Dispatch, useEffect, useState } from 'react'; +import { + Box, + Card, + IconButton, + LinearProgress, + Link, + Menu, + MenuItem, + Snackbar, + Tooltip, +} from '@material-ui/core'; +import { default as MuiAlert } from '@material-ui/lab/Alert'; +import { AzureSite } from '@backstage/plugin-azure-sites-common'; +import { Table, TableColumn } from '@backstage/core-components'; +import FlashOnIcon from '@material-ui/icons/FlashOn'; +import PublicIcon from '@material-ui/icons/Public'; +import MoreVertIcon from '@material-ui/icons/MoreVert'; +import StartIcon from '@material-ui/icons/PlayArrow'; +import StopIcon from '@material-ui/icons/Stop'; +import OpenInNewIcon from '@material-ui/icons/OpenInNew'; +import { DateTime } from 'luxon'; +import { useApi } from '@backstage/core-plugin-api'; +import { azureSiteApiRef } from '../../api'; + +type States = 'Waiting' | 'Running' | 'Paused' | 'Failed' | 'Stopped'; +type Kinds = 'app' | 'functionapp'; + +const State = ({ value }: { value: States }) => { + const colorMap = { + Waiting: '#dcbc21', + Running: 'green', + Paused: 'black', + Failed: 'red', + Stopped: 'black', + }; + return ( + + + {value} + + ); +}; + +const Kind = ({ value }: { value: Kinds }) => { + const iconMap = { + app: , + functionapp: , + }; + return ( + + {iconMap[value]} + + ); +}; + +type TableProps = { + data: AzureSite[]; + loading: boolean; +}; + +const ActionButtons = ({ + value, + onMenuItemClick, +}: { + value: AzureSite; + onMenuItemClick: Dispatch>; +}) => { + const azureApi = useApi(azureSiteApiRef); + + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + + const handleOpen = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + const start = () => { + azureApi.start({ + name: value.name, + resourceGroup: value.resourceGroup, + subscription: value.subscription, + }); + onMenuItemClick('Starting, this may take some time...'); + handleClose(); + }; + const stop = () => { + azureApi.stop({ + name: value.name, + resourceGroup: value.resourceGroup, + subscription: value.subscription, + }); + onMenuItemClick('Stopping, this may take some time...'); + handleClose(); + }; + + return ( +
+ + + + + {value.state !== 'Running' && ( + + +  Start + + )} + {value.state !== 'Stopped' && ( + + +  Stop + + )} + + +  Log Stream + + +
+ ); +}; + +/** @public */ +export const AzureSitesOverviewTable = ({ data, loading }: TableProps) => { + const [snackbarMessage, setSnackbarMessage] = useState(null); + const [isSnackbarOpen, setSnackbarOpen] = useState(false); + + const onSnackbarClose = () => { + setSnackbarMessage(null); + }; + + useEffect(() => { + setSnackbarOpen(!!snackbarMessage); + }, [snackbarMessage]); + + const columns: TableColumn[] = [ + { + width: '25px', + field: 'kind', + render: (func: AzureSite) => , + }, + { + title: 'name', + field: 'name', + highlight: true, + render: (func: AzureSite) => { + return ( + + {func.name} + + ); + }, + }, + { + title: 'location', + field: 'location', + render: (func: AzureSite) => func.location ?? 'unknown', + }, + { + title: 'status', + field: 'status', + render: (func: AzureSite) => , + }, + { + title: 'last modified', + field: 'lastModifiedDate', + render: (func: AzureSite) => + DateTime.fromISO(func.lastModifiedDate).toLocaleString( + DateTime.DATETIME_MED, + ), + }, + { + title: 'actions', + align: 'right', + sorting: false, + field: 'actions', + render: (func: AzureSite) => ( + + ), + }, + ]; + + const tableStyle = { + minWidth: '0', + width: '100%', + }; + + return ( + + + + + Azure Sites + + } + options={{ paging: true, search: false, pageSize: 10 }} + data={data} + emptyContent={} + isLoading={loading} + columns={columns} + /> + + + {snackbarMessage} + + + + ); +}; diff --git a/plugins/azure-sites/src/hooks/useServiceEntityAnnotations.ts b/plugins/azure-sites/src/hooks/useServiceEntityAnnotations.ts new file mode 100644 index 0000000000..08070dc8e4 --- /dev/null +++ b/plugins/azure-sites/src/hooks/useServiceEntityAnnotations.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; + +export const AZURE_WEB_SITE_NAME_ANNOTATION = 'azure.com/microsoft-web-sites'; +export const useServiceEntityAnnotations = (entity: Entity) => { + const webSiteName = + entity?.metadata.annotations?.[AZURE_WEB_SITE_NAME_ANNOTATION] ?? ''; + + return { + webSiteName, + }; +}; diff --git a/plugins/azure-sites/src/hooks/useSites.ts b/plugins/azure-sites/src/hooks/useSites.ts new file mode 100644 index 0000000000..fb3215c008 --- /dev/null +++ b/plugins/azure-sites/src/hooks/useSites.ts @@ -0,0 +1,72 @@ +/* + * Copyright 2022 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 useInterval from 'react-use/lib/useInterval'; +import useAsyncRetry from 'react-use/lib/useAsyncRetry'; +import { useApi, errorApiRef } from '@backstage/core-plugin-api'; +import { AzureSiteListResponse } from '@backstage/plugin-azure-sites-common'; +import { azureSiteApiRef } from '../api'; +import { useCallback } from 'react'; + +const POLLING_INTERVAL = 15000; + +export function useSites({ name }: { name: string }) { + const azureApi = useApi(azureSiteApiRef); + const errorApi = useApi(errorApiRef); + + const list = useCallback( + async () => { + return await azureApi.list({ + name: name, + }); + }, + [name], // eslint-disable-line react-hooks/exhaustive-deps + ); + const { + loading, + value: data, + error, + retry, + } = useAsyncRetry(async () => { + try { + const sites = await list(); + return sites; + } catch (e) { + if (e instanceof Error) { + if (e?.message === 'AbortError') { + errorApi.post( + new Error( + 'Timeout reaching backend plugin, please add azure-backend plugin', + ), + ); + } + errorApi.post(e); + } + return null; + } + }, []); + + useInterval(() => retry(), POLLING_INTERVAL); + + return [ + { + loading, + data, + error, + retry, + }, + ] as const; +} diff --git a/plugins/azure-sites/src/index.ts b/plugins/azure-sites/src/index.ts new file mode 100644 index 0000000000..83029c6af5 --- /dev/null +++ b/plugins/azure-sites/src/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2022 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 * from './plugin'; +export * from './api'; +export * from './components/AzureSitesOverviewComponent/AzureSitesOverview'; diff --git a/plugins/azure-sites/src/mocks/mocks.ts b/plugins/azure-sites/src/mocks/mocks.ts new file mode 100644 index 0000000000..2bc3b8b9fe --- /dev/null +++ b/plugins/azure-sites/src/mocks/mocks.ts @@ -0,0 +1,55 @@ +/* + * Copyright 2022 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 { AzureSite } from '@backstage/plugin-azure-sites-common'; + +export const entityMock = { + metadata: { + namespace: 'default', + annotations: { + 'azure.com/microsoft-web-sites': 'func-mock', + }, + name: 'sample-azure-service', + description: 'A service for testing Backstage functionality.', + uid: 'c009b513-d053-4b3f-9429-8433a145e943', + }, + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + spec: { + type: 'service', + owner: 'dev_group@example.com', + lifecycle: 'experimental', + }, +}; + +// https://management.azure.com/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroup}}/providers/Microsoft.Web/sites/{{functionsName}}?api-version=2022-03-01 +export const siteMock: AzureSite = { + name: 'func-mock', + kind: 'functionapp', + resourceGroup: 'mock-resourcegroup', + subscription: '00000000-0000-0000-0000-000000000000', + tags: { + isMock: true, + }, + location: 'West Europe', + state: 'Running', + href: 'https://mockurl.api.azurewebsites.windows.net:454/subscriptions/00000000-0000-0000-0000-000000000000/webspaces/rg_mock-WestEuropewebspace/sites/func-mock', + logstreamHref: + 'https://mockurl.api.azurewebsites.windows.net:454/subscriptions/00000000-0000-0000-0000-000000000000/webspaces/rg_mock-WestEuropewebspace/sites/func-mock/logStream', + usageState: 'Normal', + lastModifiedDate: '2022-09-02T11:09:58.9033333', + containerSize: 100, +}; diff --git a/plugins/azure-sites/src/plugin.test.ts b/plugins/azure-sites/src/plugin.test.ts new file mode 100644 index 0000000000..fba8e36e6f --- /dev/null +++ b/plugins/azure-sites/src/plugin.test.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2022 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 { azureSitesPlugin } from './plugin'; + +describe('azure', () => { + it('should export plugin', () => { + expect(azureSitesPlugin).toBeDefined(); + }); +}); diff --git a/plugins/azure-sites/src/plugin.ts b/plugins/azure-sites/src/plugin.ts new file mode 100644 index 0000000000..f54f121ea1 --- /dev/null +++ b/plugins/azure-sites/src/plugin.ts @@ -0,0 +1,61 @@ +/* + * Copyright 2022 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 { + createApiFactory, + createComponentExtension, + createPlugin, + createRouteRef, + discoveryApiRef, + identityApiRef, +} from '@backstage/core-plugin-api'; +import { azureSiteApiRef, AzureSitesApiBackendClient } from './api'; + +const entityContentRouteRef = createRouteRef({ + id: 'Azure Sites Entity Content', +}); + +/** @public */ +export const azureSitesPlugin = createPlugin({ + id: 'azureSites', + apis: [ + createApiFactory({ + api: azureSiteApiRef, + deps: { + discoveryApi: discoveryApiRef, + identityApi: identityApiRef, + }, + factory: ({ discoveryApi, identityApi }) => + new AzureSitesApiBackendClient({ discoveryApi, identityApi }), + }), + ], + routes: { + entityContent: entityContentRouteRef, + }, +}); + +/** @public */ +export const EntityAzureSitesOverviewWidget = azureSitesPlugin.provide( + createComponentExtension({ + name: 'EntityAzureSitesOverviewWidget', + component: { + lazy: () => + import( + './components/AzureSitesOverviewComponent/AzureSitesOverview' + ).then(m => m.AzureSitesOverviewWidget), + }, + }), +); diff --git a/plugins/azure-sites/src/routes.ts b/plugins/azure-sites/src/routes.ts new file mode 100644 index 0000000000..1b10ad8dde --- /dev/null +++ b/plugins/azure-sites/src/routes.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2022 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 { createRouteRef } from '@backstage/core-plugin-api'; + +export const rootRouteRef = createRouteRef({ + id: 'azure-sites', +}); diff --git a/plugins/azure-sites/src/setupTests.ts b/plugins/azure-sites/src/setupTests.ts new file mode 100644 index 0000000000..2b5b615b19 --- /dev/null +++ b/plugins/azure-sites/src/setupTests.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2022 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 '@testing-library/jest-dom'; +import 'cross-fetch/polyfill'; diff --git a/plugins/badges-backend/CHANGELOG.md b/plugins/badges-backend/CHANGELOG.md index 770491ec15..8e71d4f13f 100644 --- a/plugins/badges-backend/CHANGELOG.md +++ b/plugins/badges-backend/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-badges-backend +## 0.1.31 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/catalog-client@1.1.1 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## 0.1.31-next.2 + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + ## 0.1.31-next.1 ### Patch Changes diff --git a/plugins/badges-backend/package.json b/plugins/badges-backend/package.json index 678dc8d56e..0ca4eccccf 100644 --- a/plugins/badges-backend/package.json +++ b/plugins/badges-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges-backend", "description": "A Backstage backend plugin that generates README badges for your entities", - "version": "0.1.31-next.1", + "version": "0.1.31", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/badges/CHANGELOG.md b/plugins/badges/CHANGELOG.md index 5adc7d112b..d2f5756609 100644 --- a/plugins/badges/CHANGELOG.md +++ b/plugins/badges/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-badges +## 0.2.34 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + - @backstage/theme@0.2.16 + +## 0.2.34-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/theme@0.2.16 + ## 0.2.34-next.1 ### Patch Changes diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 0f05f6fa25..0d212b70b5 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges", "description": "A Backstage plugin that generates README badges for your entities", - "version": "0.2.34-next.1", + "version": "0.2.34", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bazaar-backend/CHANGELOG.md b/plugins/bazaar-backend/CHANGELOG.md index bb39eff121..ef7e924dcb 100644 --- a/plugins/bazaar-backend/CHANGELOG.md +++ b/plugins/bazaar-backend/CHANGELOG.md @@ -1,5 +1,79 @@ # @backstage/plugin-bazaar-backend +## 0.2.0 + +### Minor Changes + +- 8554533546: **BREAKING** The bazaar-backend `createRouter` now requires that the `identityApi` is passed to the router. + + These changes are **required** to `packages/backend/src/plugins/bazaar.ts` + + The user entity ref is now added to the members table and is taken from the requesting user using the `identityApi`. + + ```diff + import { PluginEnvironment } from '../types'; + import { createRouter } from '@backstage/plugin-bazaar-backend'; + import { Router } from 'express'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + return await createRouter({ + logger: env.logger, + config: env.config, + database: env.database, + + identity: env.identity, + }); + } + ``` + +### Patch Changes + +- f7c2855d76: Router now also has endpoint `getLatestProjects` that takes a limit of projects as prop. +- Updated dependencies + - @backstage/backend-common@0.15.2 + - @backstage/backend-test-utils@0.1.29 + - @backstage/plugin-auth-node@0.2.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + +## 0.2.0-next.2 + +### Minor Changes + +- 8554533546: **BREAKING** The bazaar-backend `createRouter` now requires that the `identityApi` is passed to the router. + + These changes are **required** to `packages/backend/src/plugins/bazaar.ts` + + The user entity ref is now added to the members table and is taken from the requesting user using the `identityApi`. + + ```diff + import { PluginEnvironment } from '../types'; + import { createRouter } from '@backstage/plugin-bazaar-backend'; + import { Router } from 'express'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + return await createRouter({ + logger: env.logger, + config: env.config, + database: env.database, + + identity: env.identity, + }); + } + ``` + +### Patch Changes + +- f7c2855d76: Router now also has endpoint `getLatestProjects` that takes a limit of projects as prop. +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/backend-test-utils@0.1.29-next.2 + - @backstage/plugin-auth-node@0.2.6-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + ## 0.1.21-next.1 ### Patch Changes diff --git a/plugins/bazaar-backend/package.json b/plugins/bazaar-backend/package.json index 131959cbd6..6ac340184e 100644 --- a/plugins/bazaar-backend/package.json +++ b/plugins/bazaar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar-backend", - "version": "0.1.21-next.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -25,6 +25,7 @@ "@backstage/backend-common": "workspace:^", "@backstage/backend-test-utils": "workspace:^", "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@types/express": "^4.17.6", "express": "^4.17.1", diff --git a/plugins/bazaar-backend/src/service/DatabaseHandler.ts b/plugins/bazaar-backend/src/service/DatabaseHandler.ts index 352866b4b5..8fbe985a10 100644 --- a/plugins/bazaar-backend/src/service/DatabaseHandler.ts +++ b/plugins/bazaar-backend/src/service/DatabaseHandler.ts @@ -175,14 +175,22 @@ export class DatabaseHandler { return await this.client('metadata').where({ id: id }).del(); } - async getProjects() { + async getProjects(limit?: number, order?: string) { const coalesce = this.client.raw( 'coalesce(count(members.item_id), 0) as members_count', ); - - return await this.client('metadata') + let get = this.client('metadata') .select([...this.columns, coalesce]) - .groupBy(this.columns) - .leftJoin('members', 'metadata.id', '=', 'members.item_id'); + .groupBy(this.columns); + if (limit) { + get = get.limit(limit); + } + if (order === 'latest') { + get = get.orderByRaw('id desc'); + } + if (order === 'random') { + get = get.orderByRaw('RANDOM()'); + } + return await get.leftJoin('members', 'metadata.id', '=', 'members.item_id'); } } diff --git a/plugins/bazaar-backend/src/service/router.ts b/plugins/bazaar-backend/src/service/router.ts index 359945f298..f902d0e8cc 100644 --- a/plugins/bazaar-backend/src/service/router.ts +++ b/plugins/bazaar-backend/src/service/router.ts @@ -92,8 +92,14 @@ export async function createRouter( response.json({ status: 'ok', data: data }); }); - router.get('/projects', async (_, response) => { - const data = await dbHandler.getProjects(); + router.get('/projects', async (request, response) => { + const limit = request.query.limit?.toString(); + const order = request.query.order?.toString(); + + const data = await dbHandler.getProjects( + limit ? parseInt(limit, 10) : undefined, + order, + ); response.json({ status: 'ok', data: data }); }); diff --git a/plugins/bazaar/CHANGELOG.md b/plugins/bazaar/CHANGELOG.md index b2d0e8a8d4..82269fc0ba 100644 --- a/plugins/bazaar/CHANGELOG.md +++ b/plugins/bazaar/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-bazaar +## 0.1.25 + +### Patch Changes + +- f7c2855d76: Added a `Overview Card` for either latest or random projects. Changed `ProjectPreview.tsx` so it take `gridSize` and `useTablePagination` as props. +- c0352bbc69: Link to the user catalog entity of a member +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/plugin-catalog@1.6.0 + - @backstage/core-components@0.11.2 + - @backstage/cli@0.20.0 + - @backstage/catalog-client@1.1.1 + - @backstage/core-plugin-api@1.0.7 + - @backstage/errors@1.1.2 + +## 0.1.25-next.2 + +### Patch Changes + +- f7c2855d76: Added a `Overview Card` for either latest or random projects. Changed `ProjectPreview.tsx` so it take `gridSize` and `useTablePagination` as props. +- c0352bbc69: Link to the user catalog entity of a member +- Updated dependencies + - @backstage/cli@0.20.0-next.2 + - @backstage/plugin-catalog@1.6.0-next.2 + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + ## 0.1.25-next.1 ### Patch Changes diff --git a/plugins/bazaar/README.md b/plugins/bazaar/README.md index eeebf6eb19..6168b0c4ba 100644 --- a/plugins/bazaar/README.md +++ b/plugins/bazaar/README.md @@ -64,6 +64,30 @@ const overviewContent = ( {/* ...other entity-cards */} ``` +Add a **Bazaar overview card** to the homepage that displays either the latest projects or random projects. In `packages/app/src/components/home/HomePage.tsx` add: + +```diff ++ import { BazaarOverviewCard } from '@backstage/plugin-bazaar'; + +export const homePage = ( + + + + + ++ ++ ++ + ++ ++ ++ + + {/* ...other homepage items */} +``` + +Specify how many projects you want through the "limit" props. In the example above 4 cards is specified. + ## How does the Bazaar work? ### Layout @@ -72,6 +96,10 @@ The latest modified Bazaar projects are displayed in the Bazaar landing page, lo ![home](media/layout.png) +The "BazaarOverviewCard" can be displayed in Backstage homepage. + +![home](media/overviewCard.png) + ### Workflow To add a project to the bazaar, simply click on the `add-project` button and fill in the form. diff --git a/plugins/bazaar/api-report.md b/plugins/bazaar/api-report.md index c6afde73f2..2e6ebe6746 100644 --- a/plugins/bazaar/api-report.md +++ b/plugins/bazaar/api-report.md @@ -8,6 +8,17 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; +// @public (undocumented) +export const BazaarOverviewCard: ( + props: BazaarOverviewCardProps, +) => JSX.Element; + +// @public (undocumented) +export type BazaarOverviewCardProps = { + order: 'latest' | 'random'; + limit: number; +}; + // @public (undocumented) export const BazaarPage: () => JSX.Element; diff --git a/plugins/bazaar/media/overviewCard.png b/plugins/bazaar/media/overviewCard.png new file mode 100644 index 0000000000..676764f6e4 Binary files /dev/null and b/plugins/bazaar/media/overviewCard.png differ diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json index c88357ef4a..522d84aeec 100644 --- a/plugins/bazaar/package.json +++ b/plugins/bazaar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar", - "version": "0.1.25-next.1", + "version": "0.1.25", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,6 +27,7 @@ "@backstage/cli": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", "@backstage/plugin-catalog": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@date-io/luxon": "1.x", diff --git a/plugins/bazaar/src/api.ts b/plugins/bazaar/src/api.ts index d2486f74dc..33455e69fc 100644 --- a/plugins/bazaar/src/api.ts +++ b/plugins/bazaar/src/api.ts @@ -20,6 +20,7 @@ import { FetchApi, IdentityApi, } from '@backstage/core-plugin-api'; +import { ResponseError } from '@backstage/errors'; export const bazaarApiRef = createApiRef({ id: 'bazaar', @@ -40,7 +41,7 @@ export interface BazaarApi { addMember(id: number, userId: string): Promise; - getProjects(): Promise; + getProjects(limit?: number, order?: string): Promise; deleteProject(id: number): Promise; } @@ -148,12 +149,20 @@ export class BazaarClient implements BazaarApi { ); } - async getProjects(): Promise { + async getProjects(limit?: number, order?: string): Promise { const baseUrl = await this.discoveryApi.getBaseUrl('bazaar'); + const params = { + ...(limit ? { limit: limit.toString() } : {}), + ...(order ? { order } : {}), + }; + const query = new URLSearchParams(params); + const url = `projects?${query.toString()}`; - return await this.fetchApi - .fetch(`${baseUrl}/projects`) - .then(resp => resp.json()); + const data = await this.fetchApi.fetch(`${baseUrl}/${url}`); + if (!data.ok) { + throw await ResponseError.fromResponse(data); + } + return data.json(); } async deleteProject(id: number): Promise { diff --git a/plugins/bazaar/src/components/BazaarOverviewCard/BazaarOverviewCard.tsx b/plugins/bazaar/src/components/BazaarOverviewCard/BazaarOverviewCard.tsx new file mode 100644 index 0000000000..83358c0a05 --- /dev/null +++ b/plugins/bazaar/src/components/BazaarOverviewCard/BazaarOverviewCard.tsx @@ -0,0 +1,141 @@ +/* + * Copyright 2022 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 React, { useEffect, useState } from 'react'; +import { ProjectPreview } from '../ProjectPreview/ProjectPreview'; +import useAsyncFn from 'react-use/lib/useAsyncFn'; +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; +import { useApi, useRouteRef } from '@backstage/core-plugin-api'; +import { catalogApiRef } from '@backstage/plugin-catalog-react'; +import type { BazaarProject } from '../../types'; +import { bazaarApiRef } from '../../api'; +import { fetchCatalogItems } from '../../util/fetchMethods'; +import { parseBazaarProject } from '../../util/parseMethods'; +import { ErrorPanel, InfoCard } from '@backstage/core-components'; +import { bazaarPlugin } from '../../plugin'; + +/** @public */ +export type BazaarOverviewCardProps = { + order: 'latest' | 'random'; + limit: number; +}; + +const getUnlinkedCatalogEntities = ( + bazaarProjects: BazaarProject[], + catalogEntities: Entity[], +) => { + const bazaarProjectRefs = bazaarProjects.map( + (project: BazaarProject) => project.entityRef, + ); + + return catalogEntities.filter((entity: Entity) => { + return !bazaarProjectRefs?.includes(stringifyEntityRef(entity)); + }); +}; + +/** @public */ +export const BazaarOverviewCard = (props: BazaarOverviewCardProps) => { + const { order, limit } = props; + const bazaarApi = useApi(bazaarApiRef); + const catalogApi = useApi(catalogApiRef); + const root = useRouteRef(bazaarPlugin.routes.root); + + const bazaarLink = { + title: 'Go to Bazaar', + link: root.toString(), + }; + + const [unlinkedCatalogEntities, setUnlinkedCatalogEntities] = + useState(); + + const [catalogEntities, fetchCatalogEntities] = useAsyncFn(async () => { + return await fetchCatalogItems(catalogApi); + }); + + const [bazaarProjects, fetchBazaarProjects] = useAsyncFn(async () => { + const response = await bazaarApi.getProjects(limit, order); + return response.data.map(parseBazaarProject) as BazaarProject[]; + }); + + const catalogEntityRefs = catalogEntities.value?.map((project: Entity) => + stringifyEntityRef(project), + ); + + useEffect(() => { + const filterBrokenLinks = () => { + if (catalogEntityRefs) { + bazaarProjects.value?.forEach(async (project: BazaarProject) => { + if (project.entityRef) { + if (!catalogEntityRefs?.includes(project.entityRef)) { + await bazaarApi.updateProject({ + ...project, + entityRef: null, + }); + } + } + }); + } + }; + filterBrokenLinks(); + }, [ + bazaarApi, + bazaarProjects.value, + catalogEntityRefs, + catalogEntities.value, + ]); + + useEffect(() => { + fetchCatalogEntities(); + fetchBazaarProjects(); + }, [fetchBazaarProjects, fetchCatalogEntities]); + + useEffect(() => { + const unlinkedCEntities = getUnlinkedCatalogEntities( + bazaarProjects.value || [], + catalogEntities.value || [], + ); + + if (unlinkedCEntities) { + setUnlinkedCatalogEntities(unlinkedCEntities); + } + }, [bazaarProjects, catalogEntities]); + + if (catalogEntities.error) { + return ; + } + + if (bazaarProjects.error) { + return ; + } + + return ( + + + + ); +}; diff --git a/plugins/bazaar/src/components/BazaarOverviewCard/index.ts b/plugins/bazaar/src/components/BazaarOverviewCard/index.ts new file mode 100644 index 0000000000..06ea623e1b --- /dev/null +++ b/plugins/bazaar/src/components/BazaarOverviewCard/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2022 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 { BazaarOverviewCard } from './BazaarOverviewCard'; +export type { BazaarOverviewCardProps } from './BazaarOverviewCard'; diff --git a/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx b/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx index 3aeba305a9..dace9b8185 100644 --- a/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx +++ b/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx @@ -30,6 +30,13 @@ import { DateTime } from 'luxon'; import { HomePageBazaarInfoCard } from '../HomePageBazaarInfoCard'; import { Entity } from '@backstage/catalog-model'; +type Props = { + project: BazaarProject; + fetchBazaarProjects: () => Promise; + catalogEntities: Entity[]; + fullHeight?: boolean; +}; + const useStyles = makeStyles({ statusTag: { display: 'inline-block', @@ -47,7 +54,7 @@ const useStyles = makeStyles({ float: 'right', }, content: { - height: '13rem', + overflow: 'scroll', }, header: { whiteSpace: 'nowrap', @@ -56,16 +63,11 @@ const useStyles = makeStyles({ }, }); -type Props = { - project: BazaarProject; - fetchBazaarProjects: () => Promise; - catalogEntities: Entity[]; -}; - export const ProjectCard = ({ project, fetchBazaarProjects, catalogEntities, + fullHeight, }: Props) => { const classes = useStyles(); const [openCard, setOpenCard] = useState(false); @@ -101,7 +103,10 @@ export const ProjectCard = ({ base: DateTime.now(), })}`} /> - + {Number(membersCount) === Number(1) diff --git a/plugins/bazaar/src/components/ProjectPreview/ProjectPreview.tsx b/plugins/bazaar/src/components/ProjectPreview/ProjectPreview.tsx index 435d80408d..77c16ef306 100644 --- a/plugins/bazaar/src/components/ProjectPreview/ProjectPreview.tsx +++ b/plugins/bazaar/src/components/ProjectPreview/ProjectPreview.tsx @@ -25,6 +25,9 @@ type Props = { bazaarProjects: BazaarProject[]; fetchBazaarProjects: () => Promise; catalogEntities: Entity[]; + useTablePagination?: boolean; + fullHeight?: boolean; + fixedWidth?: boolean; }; const useStyles = makeStyles({ @@ -51,6 +54,9 @@ export const ProjectPreview = ({ bazaarProjects, fetchBazaarProjects, catalogEntities, + useTablePagination = true, + fullHeight = true, + fixedWidth = false, }: Props) => { const classes = useStyles(); const [page, setPage] = useState(1); @@ -80,31 +86,39 @@ export const ProjectPreview = ({ .slice((page - 1) * rows, rows * page) .map((bazaarProject: BazaarProject, i: number) => { return ( - + ); })} - = bazaarProjects.length, - }} - /> + {useTablePagination && ( + = bazaarProjects.length, + }} + /> + )} ); }; diff --git a/plugins/bazaar/src/index.ts b/plugins/bazaar/src/index.ts index 895469c031..0014c66b6d 100644 --- a/plugins/bazaar/src/index.ts +++ b/plugins/bazaar/src/index.ts @@ -15,5 +15,7 @@ */ export { bazaarPlugin, BazaarPage } from './plugin'; +export { BazaarOverviewCard } from './components/BazaarOverviewCard'; +export type { BazaarOverviewCardProps } from './components/BazaarOverviewCard'; export { EntityBazaarInfoCard } from './components/EntityBazaarInfoCard'; export { SortView } from './components/SortView'; diff --git a/plugins/bitbucket-cloud-common/CHANGELOG.md b/plugins/bitbucket-cloud-common/CHANGELOG.md index 2dc4bd1ae3..c9befa2959 100644 --- a/plugins/bitbucket-cloud-common/CHANGELOG.md +++ b/plugins/bitbucket-cloud-common/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-bitbucket-cloud-common +## 0.2.0 + +### Minor Changes + +- ad74723fbf: Update Bitbucket Cloud models to latest OAS version. + + The latest specification contained some BREAKING CHANGES + due to removed fields. + + All of these fields are not used at other plugins, though. + Therefore, this change has no impact on other modules here. + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.3.2 + +## 0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.3.2-next.2 + ## 0.2.0-next.1 ### Patch Changes diff --git a/plugins/bitbucket-cloud-common/package.json b/plugins/bitbucket-cloud-common/package.json index 10d443e2a5..f7f1ffceac 100644 --- a/plugins/bitbucket-cloud-common/package.json +++ b/plugins/bitbucket-cloud-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-bitbucket-cloud-common", "description": "Common functionalities for bitbucket-cloud plugins", - "version": "0.2.0-next.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bitrise/CHANGELOG.md b/plugins/bitrise/CHANGELOG.md index d8781740ec..9e6d7a9306 100644 --- a/plugins/bitrise/CHANGELOG.md +++ b/plugins/bitrise/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-bitrise +## 0.1.37 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## 0.1.37-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + ## 0.1.37-next.1 ### Patch Changes diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index 0645020104..4240262c07 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-bitrise", "description": "A Backstage plugin that integrates towards Bitrise", - "version": "0.1.37-next.1", + "version": "0.1.37", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index 54717bb37d..47dbcd7f53 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.1.10 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## 0.1.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + ## 0.1.10-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/api-report.md b/plugins/catalog-backend-module-aws/api-report.md index b1f6358979..a911fb5ebb 100644 --- a/plugins/catalog-backend-module-aws/api-report.md +++ b/plugins/catalog-backend-module-aws/api-report.md @@ -3,6 +3,7 @@ > 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 { CatalogProcessor } from '@backstage/plugin-catalog-backend'; import { CatalogProcessorEmit } from '@backstage/plugin-catalog-backend'; import { CatalogProcessorParser } from '@backstage/plugin-catalog-backend'; @@ -12,6 +13,7 @@ import { EntityProvider } from '@backstage/plugin-catalog-backend'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; import { LocationSpec } from '@backstage/plugin-catalog-backend'; import { Logger } from 'winston'; +import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { TaskRunner } from '@backstage/backend-tasks'; import { UrlReader } from '@backstage/backend-common'; @@ -77,7 +79,8 @@ export class AwsS3EntityProvider implements EntityProvider { configRoot: Config, options: { logger: Logger; - schedule: TaskRunner; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; }, ): AwsS3EntityProvider[]; // (undocumented) @@ -85,4 +88,9 @@ export class AwsS3EntityProvider implements EntityProvider { // (undocumented) refresh(logger: Logger): Promise; } + +// @alpha +export const awsS3EntityProviderCatalogModule: ( + options?: undefined, +) => BackendFeature; ``` diff --git a/plugins/catalog-backend-module-aws/config.d.ts b/plugins/catalog-backend-module-aws/config.d.ts index 6f84ca6daf..d6efe10c90 100644 --- a/plugins/catalog-backend-module-aws/config.d.ts +++ b/plugins/catalog-backend-module-aws/config.d.ts @@ -14,11 +14,10 @@ * limitations under the License. */ +import { TaskScheduleDefinitionConfig } from '@backstage/backend-tasks'; + export interface Config { catalog?: { - /** - * List of processor-specific options and attributes - */ processors?: { /** * AwsOrganizationCloudAccountProcessor configuration @@ -32,9 +31,6 @@ export interface Config { }; }; }; - /** - * List of provider-specific options and attributes - */ providers?: { /** * AwsS3EntityProvider configuration @@ -45,22 +41,23 @@ export interface Config { | { /** * (Required) AWS S3 Bucket Name - * @visibility backend */ bucketName: string; /** * (Optional) AWS S3 Object key prefix * If not set, all keys will be accepted, no filtering will be applied. - * @visibility backend */ prefix?: string; /** * (Optional) AWS Region. * If not set, AWS_REGION environment variable or aws config file will be used. * @see https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-region.html - * @visibility backend */ region?: string; + /** + * (Optional) TaskScheduleDefinition for the refresh. + */ + schedule?: TaskScheduleDefinitionConfig; } | Record< string, @@ -83,6 +80,10 @@ export interface Config { * @visibility backend */ region?: string; + /** + * (Optional) TaskScheduleDefinition for the refresh. + */ + schedule?: TaskScheduleDefinitionConfig; } >; }; diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index 8be3ca3031..37acb57254 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,12 +1,13 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", "description": "A Backstage catalog backend module that helps integrate towards AWS", - "version": "0.1.10-next.1", + "version": "0.1.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { "access": "public", + "alphaTypes": "dist/index.alpha.d.ts", "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, @@ -23,22 +24,24 @@ "backstage" ], "scripts": { - "build": "backstage-cli package build", + "start": "backstage-cli package start", + "build": "backstage-cli package build --experimental-type-build", "lint": "backstage-cli package lint", "test": "backstage-cli package test", "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", - "clean": "backstage-cli package clean", - "start": "backstage-cli package start" + "clean": "backstage-cli package clean" }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/backend-tasks": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", "@backstage/types": "workspace:^", "aws-sdk": "^2.840.0", "lodash": "^4.17.21", @@ -47,14 +50,17 @@ "winston": "^3.2.1" }, "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@types/lodash": "^4.14.151", "aws-sdk-mock": "^5.2.1", + "luxon": "^3.0.0", "yaml": "^2.0.0" }, "files": [ - "dist", - "config.d.ts" + "alpha", + "config.d.ts", + "dist" ], "configSchema": "config.d.ts" } diff --git a/plugins/catalog-backend-module-aws/src/index.ts b/plugins/catalog-backend-module-aws/src/index.ts index 212308edaa..804fd97e91 100644 --- a/plugins/catalog-backend-module-aws/src/index.ts +++ b/plugins/catalog-backend-module-aws/src/index.ts @@ -22,4 +22,5 @@ export * from './processors'; export * from './providers'; +export { awsS3EntityProviderCatalogModule } from './service/AwsS3EntityProviderCatalogModule'; export * from './types'; diff --git a/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.test.ts b/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.test.ts index 3d59d738cf..134a3c6338 100644 --- a/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.test.ts +++ b/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.test.ts @@ -15,7 +15,11 @@ */ import { getVoidLogger } from '@backstage/backend-common'; -import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks'; +import { + PluginTaskScheduler, + TaskInvocationDefinition, + TaskRunner, +} from '@backstage/backend-tasks'; import { ConfigReader } from '@backstage/config'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; import { AwsS3EntityProvider } from './AwsS3EntityProvider'; @@ -83,6 +87,7 @@ describe('AwsS3EntityProvider', () => { expectedBaseUrl: string, names: Record, integrationConfig?: object, + scheduleInConfig?: boolean, ) => { const config = new ConfigReader({ integrations: { @@ -97,15 +102,25 @@ describe('AwsS3EntityProvider', () => { }, }); + const schedulingConfig: Record = {}; + const schedule = new PersistingTaskRunner(); const entityProviderConnection: EntityProviderConnection = { applyMutation: jest.fn(), refresh: jest.fn(), }; + if (scheduleInConfig) { + schedulingConfig.scheduler = { + createScheduledTaskRunner: (_: any) => schedule, + } as unknown as PluginTaskScheduler; + } else { + schedulingConfig.schedule = schedule; + } + const provider = AwsS3EntityProvider.fromConfig(config, { + ...schedulingConfig, logger, - schedule, })[0]; expect(provider.getProviderName()).toEqual(`awsS3-provider:${providerId}`); @@ -224,4 +239,81 @@ describe('AwsS3EntityProvider', () => { }, ); }); + + it('fail without schedule and scheduler', () => { + const config = new ConfigReader({ + catalog: { + providers: { + awsS3: { + test: { + bucketName: 'bucket-1', + prefix: 'sub/dir/', + region: 'eu-west-1', + }, + }, + }, + }, + }); + + expect(() => + AwsS3EntityProvider.fromConfig(config, { + logger, + }), + ).toThrow('Either schedule or scheduler must be provided'); + }); + + it('fail with scheduler but no schedule config', () => { + const scheduler = { + createScheduledTaskRunner: (_: any) => jest.fn(), + } as unknown as PluginTaskScheduler; + const config = new ConfigReader({ + catalog: { + providers: { + awsS3: { + test: { + bucketName: 'bucket-1', + prefix: 'sub/dir/', + region: 'eu-west-1', + }, + }, + }, + }, + }); + + expect(() => + AwsS3EntityProvider.fromConfig(config, { + logger, + scheduler, + }), + ).toThrow( + 'No schedule provided neither via code nor config for awsS3-provider:test', + ); + }); + + // eslint-disable-next-line jest/expect-expect + it('single simple provider config with schedule in config', async () => { + return expectMutation( + 'regionalStatic', + { + bucketName: 'bucket-1', + prefix: 'sub/dir/', + region: 'eu-west-1', + schedule: { + frequency: 'PT30M', + timeout: { + minutes: 3, + }, + }, + }, + 'https://s3.eu-west-1.amazonaws.com/bucket-1/sub/dir/', + { + 'key1.yaml': 'generated-7f6d5861b0b3401a38b5fe62e6c7ca11da5fd6d8', + 'key2.yaml': 'generated-a290be145586042af7d80715626399c9d661718d', + 'key3.yaml': 'generated-8d75f78ed9fa618ce433b226dc24eeab441f3a2d', + 'key 4.yaml': 'generated-1e0249dcb5805fc2ce6ac2d3c4d2a3ef4f1270c0', + }, + undefined, + true, + ); + }); }); diff --git a/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts b/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts index cee5eea1a4..80bc4d459b 100644 --- a/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts +++ b/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { TaskRunner } from '@backstage/backend-tasks'; +import { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks'; import { Config } from '@backstage/config'; import { AwsS3Integration, ScmIntegrations } from '@backstage/integration'; import { @@ -49,7 +49,8 @@ export class AwsS3EntityProvider implements EntityProvider { configRoot: Config, options: { logger: Logger; - schedule: TaskRunner; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; }, ): AwsS3EntityProvider[] { const providerConfigs = readAwsS3Configs(configRoot); @@ -67,22 +68,35 @@ export class AwsS3EntityProvider implements EntityProvider { throw new Error('No integration found for awsS3'); } - return providerConfigs.map( - providerConfig => - new AwsS3EntityProvider( - providerConfig, - integration, - options.logger, - options.schedule, - ), - ); + if (!options.schedule && !options.scheduler) { + throw new Error('Either schedule or scheduler must be provided.'); + } + + return providerConfigs.map(providerConfig => { + if (!options.schedule && !providerConfig.schedule) { + throw new Error( + `No schedule provided neither via code nor config for awsS3-provider:${providerConfig.id}.`, + ); + } + + const taskRunner = + options.schedule ?? + options.scheduler!.createScheduledTaskRunner(providerConfig.schedule!); + + return new AwsS3EntityProvider( + providerConfig, + integration, + options.logger, + taskRunner, + ); + }); } private constructor( private readonly config: AwsS3Config, integration: AwsS3Integration, logger: Logger, - schedule: TaskRunner, + taskRunner: TaskRunner, ) { this.logger = logger.child({ target: this.getProviderName(), @@ -99,13 +113,13 @@ export class AwsS3EntityProvider implements EntityProvider { s3ForcePathStyle: integration.config.s3ForcePathStyle, }); - this.scheduleFn = this.createScheduleFn(schedule); + this.scheduleFn = this.createScheduleFn(taskRunner); } - private createScheduleFn(schedule: TaskRunner): () => Promise { + private createScheduleFn(taskRunner: TaskRunner): () => Promise { return async () => { const taskId = `${this.getProviderName()}:refresh`; - return schedule.run({ + return taskRunner.run({ id: taskId, fn: async () => { const logger = this.logger.child({ diff --git a/plugins/catalog-backend-module-aws/src/providers/config.test.ts b/plugins/catalog-backend-module-aws/src/providers/config.test.ts index 534d4da35e..fb2f3c0790 100644 --- a/plugins/catalog-backend-module-aws/src/providers/config.test.ts +++ b/plugins/catalog-backend-module-aws/src/providers/config.test.ts @@ -15,6 +15,7 @@ */ import { ConfigReader } from '@backstage/config'; +import { Duration } from 'luxon'; import { readAwsS3Configs } from './config'; describe('readAwsS3Configs', () => { @@ -54,17 +55,26 @@ describe('readAwsS3Configs', () => { const provider3 = { bucketName: 'bucket-3', }; + const provider4 = { + bucketName: 'bucket-4', + schedule: { + frequency: 'PT30M', + timeout: { + minutes: 3, + }, + }, + }; const config = { catalog: { providers: { - awsS3: { provider1, provider2, provider3 }, + awsS3: { provider1, provider2, provider3, provider4 }, }, }, }; const actual = readAwsS3Configs(new ConfigReader(config)); - expect(actual).toHaveLength(3); + expect(actual).toHaveLength(4); expect(actual[0]).toEqual({ ...provider1, id: 'provider1', @@ -77,6 +87,14 @@ describe('readAwsS3Configs', () => { ...provider3, id: 'provider3', }); + expect(actual[3]).toEqual({ + ...provider4, + id: 'provider4', + schedule: { + ...provider4.schedule, + frequency: Duration.fromISO(provider4.schedule.frequency), + }, + }); }); it('fails if bucketName is missing', () => { diff --git a/plugins/catalog-backend-module-aws/src/providers/config.ts b/plugins/catalog-backend-module-aws/src/providers/config.ts index 5fdf2351c3..5ef2729bf0 100644 --- a/plugins/catalog-backend-module-aws/src/providers/config.ts +++ b/plugins/catalog-backend-module-aws/src/providers/config.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { readTaskScheduleDefinitionFromConfig } from '@backstage/backend-tasks'; import { Config } from '@backstage/config'; import { AwsS3Config } from './types'; @@ -46,10 +47,15 @@ function readAwsS3Config(id: string, config: Config): AwsS3Config { const region = config.getOptionalString('region'); const prefix = config.getOptionalString('prefix'); + const schedule = config.has('schedule') + ? readTaskScheduleDefinitionFromConfig(config.getConfig('schedule')) + : undefined; + return { id, bucketName, region, prefix, + schedule, }; } diff --git a/plugins/catalog-backend-module-aws/src/providers/types.ts b/plugins/catalog-backend-module-aws/src/providers/types.ts index 3508bf919c..204fa154c6 100644 --- a/plugins/catalog-backend-module-aws/src/providers/types.ts +++ b/plugins/catalog-backend-module-aws/src/providers/types.ts @@ -14,9 +14,12 @@ * limitations under the License. */ +import { TaskScheduleDefinition } from '@backstage/backend-tasks'; + export type AwsS3Config = { id: string; bucketName: string; prefix?: string; region?: string; + schedule?: TaskScheduleDefinition; }; diff --git a/plugins/catalog-backend-module-aws/src/service/AwsS3EntityProviderCatalogModule.test.ts b/plugins/catalog-backend-module-aws/src/service/AwsS3EntityProviderCatalogModule.test.ts new file mode 100644 index 0000000000..3167291bca --- /dev/null +++ b/plugins/catalog-backend-module-aws/src/service/AwsS3EntityProviderCatalogModule.test.ts @@ -0,0 +1,84 @@ +/* + * Copyright 2022 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 { getVoidLogger } from '@backstage/backend-common'; +import { + configServiceRef, + loggerServiceRef, + schedulerServiceRef, +} from '@backstage/backend-plugin-api'; +import { + PluginTaskScheduler, + TaskScheduleDefinition, +} from '@backstage/backend-tasks'; +import { startTestBackend } from '@backstage/backend-test-utils'; +import { ConfigReader } from '@backstage/config'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node'; +import { Duration } from 'luxon'; +import { awsS3EntityProviderCatalogModule } from './AwsS3EntityProviderCatalogModule'; +import { AwsS3EntityProvider } from '../providers'; + +describe('awsS3EntityProviderCatalogModule', () => { + it('should register provider at the catalog extension point', async () => { + let addedProviders: Array | undefined; + let usedSchedule: TaskScheduleDefinition | undefined; + + const extensionPoint = { + addEntityProvider: (providers: any) => { + addedProviders = providers; + }, + }; + const runner = jest.fn(); + const scheduler = { + createScheduledTaskRunner: (schedule: TaskScheduleDefinition) => { + usedSchedule = schedule; + return runner; + }, + } as unknown as PluginTaskScheduler; + + const config = new ConfigReader({ + catalog: { + providers: { + awsS3: { + bucketName: 'module-test', + schedule: { + frequency: 'P1M', + timeout: 'PT3M', + }, + }, + }, + }, + }); + + await startTestBackend({ + extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], + services: [ + [configServiceRef, config], + [loggerServiceRef, getVoidLogger()], + [schedulerServiceRef, scheduler], + ], + features: [awsS3EntityProviderCatalogModule()], + }); + + expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M')); + expect(usedSchedule?.timeout).toEqual(Duration.fromISO('PT3M')); + expect(addedProviders?.length).toEqual(1); + expect(addedProviders?.pop()?.getProviderName()).toEqual( + 'awsS3-provider:default', + ); + expect(runner).not.toHaveBeenCalled(); + }); +}); diff --git a/plugins/catalog-backend-module-aws/src/service/AwsS3EntityProviderCatalogModule.ts b/plugins/catalog-backend-module-aws/src/service/AwsS3EntityProviderCatalogModule.ts new file mode 100644 index 0000000000..79269ad53a --- /dev/null +++ b/plugins/catalog-backend-module-aws/src/service/AwsS3EntityProviderCatalogModule.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2022 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 { + createBackendModule, + loggerToWinstonLogger, + configServiceRef, + loggerServiceRef, + schedulerServiceRef, +} from '@backstage/backend-plugin-api'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node'; +import { AwsS3EntityProvider } from '../providers'; + +/** + * Registers the AwsS3EntityProvider with the catalog processing extension point. + * + * @alpha + */ +export const awsS3EntityProviderCatalogModule = createBackendModule({ + pluginId: 'catalog', + moduleId: 'awsS3EntityProvider', + register(env) { + env.registerInit({ + deps: { + config: configServiceRef, + catalog: catalogProcessingExtensionPoint, + logger: loggerServiceRef, + scheduler: schedulerServiceRef, + }, + async init({ config, catalog, logger, scheduler }) { + catalog.addEntityProvider( + AwsS3EntityProvider.fromConfig(config, { + logger: loggerToWinstonLogger(logger), + scheduler, + }), + ); + }, + }); + }, +}); diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index 4342307cf5..7c40b06228 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.1.8 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## 0.1.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + ## 0.1.8-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/api-report.md b/plugins/catalog-backend-module-azure/api-report.md index 22f5015c9b..84ffaf39e9 100644 --- a/plugins/catalog-backend-module-azure/api-report.md +++ b/plugins/catalog-backend-module-azure/api-report.md @@ -3,6 +3,7 @@ > 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 { CatalogProcessor } from '@backstage/plugin-catalog-backend'; import { CatalogProcessorEmit } from '@backstage/plugin-catalog-backend'; import { Config } from '@backstage/config'; @@ -10,6 +11,7 @@ import { EntityProvider } from '@backstage/plugin-catalog-backend'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; import { LocationSpec } from '@backstage/plugin-catalog-backend'; import { Logger } from 'winston'; +import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { TaskRunner } from '@backstage/backend-tasks'; @@ -45,7 +47,8 @@ export class AzureDevOpsEntityProvider implements EntityProvider { configRoot: Config, options: { logger: Logger; - schedule: TaskRunner; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; }, ): AzureDevOpsEntityProvider[]; // (undocumented) @@ -53,4 +56,9 @@ export class AzureDevOpsEntityProvider implements EntityProvider { // (undocumented) refresh(logger: Logger): Promise; } + +// @alpha +export const azureDevOpsEntityProviderCatalogModule: ( + options?: undefined, +) => BackendFeature; ``` diff --git a/plugins/catalog-backend-module-azure/config.d.ts b/plugins/catalog-backend-module-azure/config.d.ts index 6e423c9415..940695c8ea 100644 --- a/plugins/catalog-backend-module-azure/config.d.ts +++ b/plugins/catalog-backend-module-azure/config.d.ts @@ -14,34 +14,35 @@ * limitations under the License. */ +import { TaskScheduleDefinitionConfig } from '@backstage/backend-tasks'; + interface AzureDevOpsConfig { /** * (Optional) The DevOps host; leave empty for `dev.azure.com`, otherwise set to your self-hosted instance host. - * @visibility backend */ host: string; /** * (Required) Your organization slug. - * @visibility backend */ organization: string; /** * (Required) Your project slug. - * @visibility backend */ project: string; /** * (Optional) The repository name. Wildcards are supported as show on the examples above. * If not set, all repositories will be searched. - * @visibility backend */ repository?: string; /** * (Optional) Where to find catalog-info.yaml files. Wildcards are supported. * If not set, defaults to /catalog-info.yaml. - * @visibility backend */ path?: string; + /** + * (Optional) TaskScheduleDefinition for the refresh. + */ + schedule?: TaskScheduleDefinitionConfig; } export interface Config { diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index 0385655259..b4396a32af 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -1,12 +1,13 @@ { "name": "@backstage/plugin-catalog-backend-module-azure", "description": "A Backstage catalog backend module that helps integrate towards Azure", - "version": "0.1.8-next.1", + "version": "0.1.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { "access": "public", + "alphaTypes": "dist/index.alpha.d.ts", "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, @@ -23,22 +24,24 @@ "backstage" ], "scripts": { - "build": "backstage-cli package build", + "start": "backstage-cli package start", + "build": "backstage-cli package build --experimental-type-build", "lint": "backstage-cli package lint", "test": "backstage-cli package test", "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", - "clean": "backstage-cli package clean", - "start": "backstage-cli package start" + "clean": "backstage-cli package clean" }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/backend-tasks": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", "@backstage/types": "workspace:^", "lodash": "^4.17.21", "msw": "^0.47.0", @@ -49,11 +52,13 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", - "@types/lodash": "^4.14.151" + "@types/lodash": "^4.14.151", + "luxon": "^3.0.0" }, "files": [ - "dist", - "config.d.ts" + "alpha", + "config.d.ts", + "dist" ], "configSchema": "config.d.ts" } diff --git a/plugins/catalog-backend-module-azure/src/index.ts b/plugins/catalog-backend-module-azure/src/index.ts index a71c40ee6c..aa3a94fda2 100644 --- a/plugins/catalog-backend-module-azure/src/index.ts +++ b/plugins/catalog-backend-module-azure/src/index.ts @@ -22,3 +22,4 @@ export { AzureDevOpsDiscoveryProcessor } from './processors'; export { AzureDevOpsEntityProvider } from './providers'; +export { azureDevOpsEntityProviderCatalogModule } from './service/AzureDevOpsEntityProviderCatalogModule'; diff --git a/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.test.ts b/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.test.ts index c57455d8d6..f7a63f259e 100644 --- a/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.test.ts +++ b/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.test.ts @@ -15,7 +15,11 @@ */ import { getVoidLogger } from '@backstage/backend-common'; -import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks'; +import { + PluginTaskScheduler, + TaskInvocationDefinition, + TaskRunner, +} from '@backstage/backend-tasks'; import { ConfigReader } from '@backstage/config'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; import { CodeSearchResultItem } from '../lib'; @@ -52,6 +56,7 @@ describe('AzureDevOpsEntityProvider', () => { expectedBaseUrl: string, names: Record, integrationConfig?: object, + scheduleInConfig?: boolean, ) => { const config = new ConfigReader({ integrations: { @@ -74,9 +79,18 @@ describe('AzureDevOpsEntityProvider', () => { refresh: jest.fn(), }; + const schedulingConfig: Record = {}; + if (scheduleInConfig) { + schedulingConfig.scheduler = { + createScheduledTaskRunner: (_: any) => schedule, + } as unknown as PluginTaskScheduler; + } else { + schedulingConfig.schedule = schedule; + } + const provider = AzureDevOpsEntityProvider.fromConfig(config, { + ...schedulingConfig, logger, - schedule, })[0]; expect(provider.getProviderName()).toEqual( `AzureDevOpsEntityProvider:${providerId}`, @@ -193,4 +207,92 @@ describe('AzureDevOpsEntityProvider', () => { }, ); }); + + it('fail without schedule and scheduler', () => { + const config = new ConfigReader({ + catalog: { + providers: { + azureDevOps: { + test: { + organization: 'myorganization', + project: 'myproject', + }, + }, + }, + }, + }); + + expect(() => + AzureDevOpsEntityProvider.fromConfig(config, { + logger, + }), + ).toThrow('Either schedule or scheduler must be provided'); + }); + + it('fail with scheduler but no schedule config', () => { + const scheduler = { + createScheduledTaskRunner: (_: any) => jest.fn(), + } as unknown as PluginTaskScheduler; + const config = new ConfigReader({ + catalog: { + providers: { + azureDevOps: { + test: { + organization: 'myorganization', + project: 'myproject', + }, + }, + }, + }, + }); + + expect(() => + AzureDevOpsEntityProvider.fromConfig(config, { + logger, + scheduler, + }), + ).toThrow( + 'No schedule provided neither via code nor config for AzureDevOpsEntityProvider:test', + ); + }); + + // eslint-disable-next-line jest/expect-expect + it('single simple provider config with schedule in config', async () => { + return expectMutation( + 'allReposMultipleFiles', + { + organization: 'myorganization', + project: 'myproject', + schedule: { + frequency: 'PT30M', + timeout: { + minutes: 3, + }, + }, + }, + [ + { + fileName: 'catalog-info.yaml', + path: '/catalog-info.yaml', + repository: { + name: 'myrepo', + }, + }, + { + fileName: 'catalog-info.yaml', + path: '/catalog-info.yaml', + repository: { + name: 'myotherrepo', + }, + }, + ], + 'https://dev.azure.com/myorganization/myproject', + { + 'myrepo?path=/catalog-info.yaml': + 'generated-87865246726bb12a8c4fb4f914443f1fbb91648c', + 'myotherrepo?path=/catalog-info.yaml': + 'generated-2deccac384c34d0dca37be0ebb4b1c8cf6913fe1', + }, + ); + }); }); diff --git a/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.ts b/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.ts index aca30f367a..b824d176ee 100644 --- a/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.ts +++ b/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { TaskRunner } from '@backstage/backend-tasks'; +import { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks'; import { Config } from '@backstage/config'; import { AzureIntegration, ScmIntegrations } from '@backstage/integration'; import { @@ -45,11 +45,16 @@ export class AzureDevOpsEntityProvider implements EntityProvider { configRoot: Config, options: { logger: Logger; - schedule: TaskRunner; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; }, ): AzureDevOpsEntityProvider[] { const providerConfigs = readAzureDevOpsConfigs(configRoot); + if (!options.schedule && !options.scheduler) { + throw new Error('Either schedule or scheduler must be provided.'); + } + return providerConfigs.map(providerConfig => { const integration = ScmIntegrations.fromConfig(configRoot).azure.byHost( providerConfig.host, @@ -61,11 +66,21 @@ export class AzureDevOpsEntityProvider implements EntityProvider { ); } + if (!options.schedule && !providerConfig.schedule) { + throw new Error( + `No schedule provided neither via code nor config for AzureDevOpsEntityProvider:${providerConfig.id}.`, + ); + } + + const taskRunner = + options.schedule ?? + options.scheduler!.createScheduledTaskRunner(providerConfig.schedule!); + return new AzureDevOpsEntityProvider( providerConfig, integration, options.logger, - options.schedule, + taskRunner, ); }); } @@ -74,19 +89,19 @@ export class AzureDevOpsEntityProvider implements EntityProvider { private readonly config: AzureDevOpsConfig, private readonly integration: AzureIntegration, logger: Logger, - schedule: TaskRunner, + taskRunner: TaskRunner, ) { this.logger = logger.child({ target: this.getProviderName(), }); - this.scheduleFn = this.createScheduleFn(schedule); + this.scheduleFn = this.createScheduleFn(taskRunner); } - private createScheduleFn(schedule: TaskRunner): () => Promise { + private createScheduleFn(taskRunner: TaskRunner): () => Promise { return async () => { const taskId = `${this.getProviderName()}:refresh`; - return schedule.run({ + return taskRunner.run({ id: taskId, fn: async () => { const logger = this.logger.child({ diff --git a/plugins/catalog-backend-module-azure/src/providers/config.test.ts b/plugins/catalog-backend-module-azure/src/providers/config.test.ts index 236c9a48d4..a09079f1c3 100644 --- a/plugins/catalog-backend-module-azure/src/providers/config.test.ts +++ b/plugins/catalog-backend-module-azure/src/providers/config.test.ts @@ -15,6 +15,7 @@ */ import { ConfigReader } from '@backstage/config'; +import { Duration } from 'luxon'; import { readAzureDevOpsConfigs } from './config'; describe('readAzureDevOpsConfigs', () => { @@ -33,17 +34,27 @@ describe('readAzureDevOpsConfigs', () => { project: 'myproject', repository: 'service-*', }; + const provider4 = { + organization: 'mycompany', + project: 'myproject', + schedule: { + frequency: 'PT30M', + timeout: { + minutes: 3, + }, + }, + }; const config = { catalog: { providers: { - azureDevOps: { provider1, provider2, provider3 }, + azureDevOps: { provider1, provider2, provider3, provider4 }, }, }, }; const actual = readAzureDevOpsConfigs(new ConfigReader(config)); - expect(actual).toHaveLength(3); + expect(actual).toHaveLength(4); expect(actual[0]).toEqual({ ...provider1, path: '/catalog-info.yaml', @@ -63,5 +74,16 @@ describe('readAzureDevOpsConfigs', () => { path: '/catalog-info.yaml', id: 'provider3', }); + expect(actual[3]).toEqual({ + ...provider4, + host: 'dev.azure.com', + path: '/catalog-info.yaml', + repository: '*', + id: 'provider4', + schedule: { + ...provider4.schedule, + frequency: Duration.fromISO(provider4.schedule.frequency), + }, + }); }); }); diff --git a/plugins/catalog-backend-module-azure/src/providers/config.ts b/plugins/catalog-backend-module-azure/src/providers/config.ts index 1d143fc882..c84c242488 100644 --- a/plugins/catalog-backend-module-azure/src/providers/config.ts +++ b/plugins/catalog-backend-module-azure/src/providers/config.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { readTaskScheduleDefinitionFromConfig } from '@backstage/backend-tasks'; import { Config } from '@backstage/config'; import { AzureDevOpsConfig } from './types'; @@ -42,6 +43,10 @@ function readAzureDevOpsConfig(id: string, config: Config): AzureDevOpsConfig { const repository = config.getOptionalString('repository') || '*'; const path = config.getOptionalString('path') || '/catalog-info.yaml'; + const schedule = config.has('schedule') + ? readTaskScheduleDefinitionFromConfig(config.getConfig('schedule')) + : undefined; + return { id, host, @@ -49,5 +54,6 @@ function readAzureDevOpsConfig(id: string, config: Config): AzureDevOpsConfig { project, repository, path, + schedule, }; } diff --git a/plugins/catalog-backend-module-azure/src/providers/types.ts b/plugins/catalog-backend-module-azure/src/providers/types.ts index 15ea00ff13..b22a8551a5 100644 --- a/plugins/catalog-backend-module-azure/src/providers/types.ts +++ b/plugins/catalog-backend-module-azure/src/providers/types.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { TaskScheduleDefinition } from '@backstage/backend-tasks'; + export type AzureDevOpsConfig = { id: string; host: string; @@ -21,4 +23,5 @@ export type AzureDevOpsConfig = { project: string; repository: string; path: string; + schedule?: TaskScheduleDefinition; }; diff --git a/plugins/catalog-backend-module-azure/src/service/AzureDevOpsEntityProviderCatalogModule.test.ts b/plugins/catalog-backend-module-azure/src/service/AzureDevOpsEntityProviderCatalogModule.test.ts new file mode 100644 index 0000000000..e3a0449a8e --- /dev/null +++ b/plugins/catalog-backend-module-azure/src/service/AzureDevOpsEntityProviderCatalogModule.test.ts @@ -0,0 +1,87 @@ +/* + * Copyright 2022 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 { getVoidLogger } from '@backstage/backend-common'; +import { + configServiceRef, + loggerServiceRef, + schedulerServiceRef, +} from '@backstage/backend-plugin-api'; +import { + PluginTaskScheduler, + TaskScheduleDefinition, +} from '@backstage/backend-tasks'; +import { startTestBackend } from '@backstage/backend-test-utils'; +import { ConfigReader } from '@backstage/config'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node'; +import { Duration } from 'luxon'; +import { azureDevOpsEntityProviderCatalogModule } from './AzureDevOpsEntityProviderCatalogModule'; +import { AzureDevOpsEntityProvider } from '../providers'; + +describe('azureDevOpsEntityProviderCatalogModule', () => { + it('should register provider at the catalog extension point', async () => { + let addedProviders: Array | undefined; + let usedSchedule: TaskScheduleDefinition | undefined; + + const extensionPoint = { + addEntityProvider: (providers: any) => { + addedProviders = providers; + }, + }; + const runner = jest.fn(); + const scheduler = { + createScheduledTaskRunner: (schedule: TaskScheduleDefinition) => { + usedSchedule = schedule; + return runner; + }, + } as unknown as PluginTaskScheduler; + + const config = new ConfigReader({ + catalog: { + providers: { + azureDevOps: { + test: { + organization: 'myorganization', + project: 'myproject', + schedule: { + frequency: 'P1M', + timeout: 'PT3M', + }, + }, + }, + }, + }, + }); + + await startTestBackend({ + extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], + services: [ + [configServiceRef, config], + [loggerServiceRef, getVoidLogger()], + [schedulerServiceRef, scheduler], + ], + features: [azureDevOpsEntityProviderCatalogModule()], + }); + + expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M')); + expect(usedSchedule?.timeout).toEqual(Duration.fromISO('PT3M')); + expect(addedProviders?.length).toEqual(1); + expect(addedProviders?.pop()?.getProviderName()).toEqual( + 'AzureDevOpsEntityProvider:test', + ); + expect(runner).not.toHaveBeenCalled(); + }); +}); diff --git a/plugins/catalog-backend-module-azure/src/service/AzureDevOpsEntityProviderCatalogModule.ts b/plugins/catalog-backend-module-azure/src/service/AzureDevOpsEntityProviderCatalogModule.ts new file mode 100644 index 0000000000..f3621bee23 --- /dev/null +++ b/plugins/catalog-backend-module-azure/src/service/AzureDevOpsEntityProviderCatalogModule.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2022 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 { + createBackendModule, + loggerToWinstonLogger, + configServiceRef, + loggerServiceRef, + schedulerServiceRef, +} from '@backstage/backend-plugin-api'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node'; +import { AzureDevOpsEntityProvider } from '../providers'; + +/** + * Registers the AzureDevOpsEntityProvider with the catalog processing extension point. + * + * @alpha + */ +export const azureDevOpsEntityProviderCatalogModule = createBackendModule({ + pluginId: 'catalog', + moduleId: 'azureDevOpsEntityProvider', + register(env) { + env.registerInit({ + deps: { + config: configServiceRef, + catalog: catalogProcessingExtensionPoint, + logger: loggerServiceRef, + scheduler: schedulerServiceRef, + }, + async init({ config, catalog, logger, scheduler }) { + catalog.addEntityProvider( + AzureDevOpsEntityProvider.fromConfig(config, { + logger: loggerToWinstonLogger(logger), + scheduler, + }), + ); + }, + }); + }, +}); diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index bbc999591c..a024b12275 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.1.4 + +### Patch Changes + +- f66e696e7b: Bitbucket Cloud provider: Add option to configure schedule via `app-config.yaml` instead of in code. + + Please find how to configure the schedule at the config at + https://backstage.io/docs/integrations/bitbucketCloud/discovery + +- a9b91d39bb: Add `bitbucketCloudCatalogModule` (new backend-plugin-api, alpha). +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/backend-plugin-api@0.1.3 + - @backstage/plugin-bitbucket-cloud-common@0.2.0 + - @backstage/config@1.0.3 + - @backstage/integration@1.3.2 + +## 0.1.4-next.2 + +### Patch Changes + +- f66e696e7b: Bitbucket Cloud provider: Add option to configure schedule via `app-config.yaml` instead of in code. + + Please find how to configure the schedule at the config at + https://backstage.io/docs/integrations/bitbucketCloud/discovery + +- a9b91d39bb: Add `bitbucketCloudCatalogModule` (new backend-plugin-api, alpha). +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0-next.2 + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/plugin-bitbucket-cloud-common@0.2.0-next.2 + ## 0.1.4-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index 54603b128f..f3564463cc 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", - "version": "0.1.4-next.1", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index ca248ce215..9c9b61247f 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.1.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + +## 0.1.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + ## 0.1.2-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/api-report.md b/plugins/catalog-backend-module-bitbucket-server/api-report.md index 7d95e148a3..a38a4d8c35 100644 --- a/plugins/catalog-backend-module-bitbucket-server/api-report.md +++ b/plugins/catalog-backend-module-bitbucket-server/api-report.md @@ -3,6 +3,7 @@ > 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 { BitbucketServerIntegrationConfig } from '@backstage/integration'; import { Config } from '@backstage/config'; import { Entity } from '@backstage/catalog-model'; @@ -10,6 +11,7 @@ import { EntityProvider } from '@backstage/plugin-catalog-backend'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; import { LocationSpec } from '@backstage/plugin-catalog-backend'; import { Logger } from 'winston'; +import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { Response as Response_2 } from 'node-fetch'; import { TaskRunner } from '@backstage/backend-tasks'; @@ -55,8 +57,9 @@ export class BitbucketServerEntityProvider implements EntityProvider { config: Config, options: { logger: Logger; - schedule: TaskRunner; parser?: BitbucketServerLocationParser; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; }, ): BitbucketServerEntityProvider[]; // (undocumented) @@ -65,6 +68,11 @@ export class BitbucketServerEntityProvider implements EntityProvider { refresh(logger: Logger): Promise; } +// @alpha (undocumented) +export const bitbucketServerEntityProviderCatalogModule: ( + options?: undefined, +) => BackendFeature; + // @public (undocumented) export type BitbucketServerListOptions = { [key: string]: number | undefined; diff --git a/plugins/catalog-backend-module-bitbucket-server/config.d.ts b/plugins/catalog-backend-module-bitbucket-server/config.d.ts index d8a09c92a4..67f694d419 100644 --- a/plugins/catalog-backend-module-bitbucket-server/config.d.ts +++ b/plugins/catalog-backend-module-bitbucket-server/config.d.ts @@ -14,11 +14,10 @@ * limitations under the License. */ +import { TaskScheduleDefinitionConfig } from '@backstage/backend-tasks'; + export interface Config { catalog?: { - /** - * List of provider-specific options and attributes - */ providers?: { /** * BitbucketServerEntityProvider configuration @@ -48,6 +47,10 @@ export interface Config { */ projectKey?: RegExp; }; + /** + * (Optional) TaskScheduleDefinition for the refresh. + */ + schedule?: TaskScheduleDefinitionConfig; } | Record< string, @@ -73,6 +76,10 @@ export interface Config { */ projectKey?: RegExp; }; + /** + * (Optional) TaskScheduleDefinition for the refresh. + */ + schedule?: TaskScheduleDefinitionConfig; } >; }; diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 2490b79ffa..30d5cef631 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -1,11 +1,12 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-server", - "version": "0.1.2-next.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { "access": "public", + "alphaTypes": "dist/index.alpha.d.ts", "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, @@ -23,21 +24,23 @@ ], "scripts": { "start": "backstage-cli package start", - "build": "backstage-cli package build", + "build": "backstage-cli package build --experimental-type-build", "lint": "backstage-cli package lint", "test": "backstage-cli package test", - "clean": "backstage-cli package clean", "prepack": "backstage-cli package prepack", - "postpack": "backstage-cli package postpack" + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean" }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/backend-tasks": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", "@types/node-fetch": "^2.5.12", "node-fetch": "^2.6.7", "uuid": "^8.0.0", @@ -46,11 +49,13 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", + "luxon": "^3.0.0", "msw": "^0.47.0" }, "files": [ - "dist", - "config.d.ts" + "alpha", + "config.d.ts", + "dist" ], "configSchema": "config.d.ts" } diff --git a/plugins/catalog-backend-module-bitbucket-server/src/index.ts b/plugins/catalog-backend-module-bitbucket-server/src/index.ts index 34ac3a9966..f139078cfb 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/index.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/index.ts @@ -29,3 +29,4 @@ export type { } from './lib'; export { BitbucketServerEntityProvider } from './providers'; export type { BitbucketServerLocationParser } from './providers'; +export { bitbucketServerEntityProviderCatalogModule } from './service/BitbucketServerEntityProviderCatalogModule'; diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts index 39689b6c1d..20299e3452 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts @@ -15,14 +15,18 @@ */ import { getVoidLogger } from '@backstage/backend-common'; -import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks'; +import { + PluginTaskScheduler, + TaskInvocationDefinition, + TaskRunner, +} from '@backstage/backend-tasks'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; import { ConfigReader } from '@backstage/config'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; -import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; -import { BitbucketServerEntityProvider } from './BitbucketServerEntityProvider'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; -import { BitbucketServerPagedResponse } from '../lib/BitbucketServerClient'; +import { BitbucketServerEntityProvider } from './BitbucketServerEntityProvider'; +import { BitbucketServerPagedResponse } from '../lib'; class PersistingTaskRunner implements TaskRunner { private tasks: TaskInvocationDefinition[] = []; @@ -367,4 +371,163 @@ describe('BitbucketServerEntityProvider', () => { entities: expectedEntities, }); }); + + it('fail without schedule and scheduler', () => { + const config = new ConfigReader({ + catalog: { + providers: { + bitbucketServer: { + host: 'bitbucket.mycompany.com', + }, + }, + }, + integrations: { + bitbucketServer: [ + { + host: 'bitbucket.mycompany.com', + }, + ], + }, + }); + + expect(() => + BitbucketServerEntityProvider.fromConfig(config, { + logger, + }), + ).toThrow('Either schedule or scheduler must be provided'); + }); + + it('fail with scheduler but no schedule config', () => { + const scheduler = { + createScheduledTaskRunner: (_: any) => jest.fn(), + } as unknown as PluginTaskScheduler; + const config = new ConfigReader({ + catalog: { + providers: { + bitbucketServer: { + host: 'bitbucket.mycompany.com', + }, + }, + }, + integrations: { + bitbucketServer: [ + { + host: 'bitbucket.mycompany.com', + }, + ], + }, + }); + + expect(() => + BitbucketServerEntityProvider.fromConfig(config, { + logger, + scheduler, + }), + ).toThrow( + 'No schedule provided neither via code nor config for bitbucketServer-provider:default', + ); + }); + + it('apply full update with schedule in config', async () => { + const host = 'bitbucket.mycompany.com'; + const config = new ConfigReader({ + integrations: { + bitbucketServer: [ + { + host: host, + }, + ], + }, + catalog: { + providers: { + bitbucketServer: { + mainProvider: { + host: host, + schedule: { + frequency: 'PT30M', + timeout: { + minutes: 3, + }, + }, + }, + }, + }, + }, + }); + const schedule = new PersistingTaskRunner(); + const scheduler = { + createScheduledTaskRunner: (_: any) => schedule, + } as unknown as PluginTaskScheduler; + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + const provider = BitbucketServerEntityProvider.fromConfig(config, { + logger, + scheduler, + })[0]; + expect(provider.getProviderName()).toEqual( + 'bitbucketServer-provider:mainProvider', + ); + + setupStubs( + [ + { key: 'project-test', repos: ['repo-test'] }, + { key: 'other-project', repos: ['other-repo'] }, + ], + `https://${host}`, + ); + await provider.connect(entityProviderConnection); + + const taskDef = schedule.getTasks()[0]; + expect(taskDef.id).toEqual('bitbucketServer-provider:mainProvider:refresh'); + await (taskDef.fn as () => Promise)(); + + const expectedEntities = [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Location', + metadata: { + annotations: { + 'backstage.io/managed-by-location': `url:https://${host}/projects/project-test/repos/repo-test/browse/catalog-info.yaml`, + 'backstage.io/managed-by-origin-location': `url:https://${host}/projects/project-test/repos/repo-test/browse/catalog-info.yaml`, + }, + name: 'generated-77f4323822420990f8c3e3c981d38c2dec4ae3a6', + }, + spec: { + presence: 'optional', + target: `https://${host}/projects/project-test/repos/repo-test/browse/catalog-info.yaml`, + type: 'url', + }, + }, + locationKey: 'bitbucketServer-provider:mainProvider', + }, + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Location', + metadata: { + annotations: { + 'backstage.io/managed-by-location': `url:https://${host}/projects/other-project/repos/other-repo/browse/catalog-info.yaml`, + 'backstage.io/managed-by-origin-location': `url:https://${host}/projects/other-project/repos/other-repo/browse/catalog-info.yaml`, + }, + name: 'generated-d8d4944c30c2906dfee172ddda9537f9893b2c0f', + }, + spec: { + presence: 'optional', + target: `https://${host}/projects/other-project/repos/other-repo/browse/catalog-info.yaml`, + type: 'url', + }, + }, + locationKey: 'bitbucketServer-provider:mainProvider', + }, + ]; + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ + type: 'full', + entities: expectedEntities, + }); + }); }); diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts index 92cecf642a..447b71ee58 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts @@ -14,29 +14,29 @@ * limitations under the License. */ +import { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks'; +import { Entity } from '@backstage/catalog-model'; +import { Config } from '@backstage/config'; +import { InputError } from '@backstage/errors'; +import { + BitbucketServerIntegration, + ScmIntegrations, +} from '@backstage/integration'; import { EntityProvider, EntityProviderConnection, } from '@backstage/plugin-catalog-backend'; import { Logger } from 'winston'; -import { Config } from '@backstage/config'; -import { TaskRunner } from '@backstage/backend-tasks'; import * as uuid from 'uuid'; -import { - BitbucketServerLocationParser, - defaultBitbucketServerLocationParser, -} from './BitbucketServerLocationParser'; -import { - BitbucketServerIntegration, - ScmIntegrations, -} from '@backstage/integration'; import { BitbucketServerClient, paginated } from '../lib'; -import { InputError } from '@backstage/errors'; import { BitbucketServerEntityProviderConfig, readProviderConfigs, } from './BitbucketServerEntityProviderConfig'; -import { Entity } from '@backstage/catalog-model'; +import { + BitbucketServerLocationParser, + defaultBitbucketServerLocationParser, +} from './BitbucketServerLocationParser'; /** * Discovers catalog files located in Bitbucket Server. @@ -58,12 +58,17 @@ export class BitbucketServerEntityProvider implements EntityProvider { config: Config, options: { logger: Logger; - schedule: TaskRunner; parser?: BitbucketServerLocationParser; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; }, ): BitbucketServerEntityProvider[] { const integrations = ScmIntegrations.fromConfig(config); + if (!options.schedule && !options.scheduler) { + throw new Error('Either schedule or scheduler must be provided.'); + } + return readProviderConfigs(config).map(providerConfig => { const integration = integrations.bitbucketServer.byHost( providerConfig.host, @@ -73,11 +78,22 @@ export class BitbucketServerEntityProvider implements EntityProvider { `No BitbucketServer integration found that matches host ${providerConfig.host}`, ); } + + if (!options.schedule && !providerConfig.schedule) { + throw new Error( + `No schedule provided neither via code nor config for bitbucketServer-provider:${providerConfig.id}.`, + ); + } + + const taskRunner = + options.schedule ?? + options.scheduler!.createScheduledTaskRunner(providerConfig.schedule!); + return new BitbucketServerEntityProvider( providerConfig, integration, options.logger, - options.schedule, + taskRunner, options.parser, ); }); @@ -87,7 +103,7 @@ export class BitbucketServerEntityProvider implements EntityProvider { config: BitbucketServerEntityProviderConfig, integration: BitbucketServerIntegration, logger: Logger, - schedule: TaskRunner, + taskRunner: TaskRunner, parser?: BitbucketServerLocationParser, ) { this.integration = integration; @@ -96,13 +112,13 @@ export class BitbucketServerEntityProvider implements EntityProvider { this.logger = logger.child({ target: this.getProviderName(), }); - this.scheduleFn = this.createScheduleFn(schedule); + this.scheduleFn = this.createScheduleFn(taskRunner); } - private createScheduleFn(schedule: TaskRunner): () => Promise { + private createScheduleFn(taskRunner: TaskRunner): () => Promise { return async () => { const taskId = `${this.getProviderName()}:refresh`; - return schedule.run({ + return taskRunner.run({ id: taskId, fn: async () => { const logger = this.logger.child({ diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.test.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.test.ts index 712b26cbb0..af8639566f 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.test.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.test.ts @@ -15,6 +15,7 @@ */ import { ConfigReader } from '@backstage/config'; +import { Duration } from 'luxon'; import { readProviderConfigs } from './BitbucketServerEntityProviderConfig'; describe('readProviderConfigs', () => { @@ -63,13 +64,22 @@ describe('readProviderConfigs', () => { host: 'bitbucket2.mycompany.com', catalogPath: 'custom/path/catalog-info.yaml', }, + thirdProvider: { + host: 'bitbucket3.mycompany.com', + schedule: { + frequency: 'PT30M', + timeout: { + minutes: 3, + }, + }, + }, }, }, }, }); const providerConfigs = readProviderConfigs(config); - expect(providerConfigs).toHaveLength(2); + expect(providerConfigs).toHaveLength(3); expect(providerConfigs[0]).toEqual({ id: 'mainProvider', catalogPath: '/catalog-info.yaml', @@ -88,6 +98,21 @@ describe('readProviderConfigs', () => { repoSlug: undefined, }, }); + expect(providerConfigs[2]).toEqual({ + id: 'thirdProvider', + catalogPath: '/catalog-info.yaml', + host: 'bitbucket3.mycompany.com', + filters: { + projectKey: undefined, + repoSlug: undefined, + }, + schedule: { + frequency: Duration.fromISO('PT30M'), + timeout: { + minutes: 3, + }, + }, + }); }); it('single provider config with filters', () => { diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.ts index 8a3decab43..b2ed7a8556 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.ts @@ -14,6 +14,10 @@ * limitations under the License. */ +import { + readTaskScheduleDefinitionFromConfig, + TaskScheduleDefinition, +} from '@backstage/backend-tasks'; import { Config } from '@backstage/config'; const DEFAULT_CATALOG_PATH = '/catalog-info.yaml'; @@ -27,6 +31,7 @@ export type BitbucketServerEntityProviderConfig = { projectKey?: RegExp; repoSlug?: RegExp; }; + schedule?: TaskScheduleDefinition; }; export function readProviderConfigs( @@ -60,6 +65,10 @@ function readProviderConfig( const projectKeyPattern = config.getOptionalString('filters.projectKey'); const repoSlugPattern = config.getOptionalString('filters.repoSlug'); + const schedule = config.has('schedule') + ? readTaskScheduleDefinitionFromConfig(config.getConfig('schedule')) + : undefined; + return { id, host, @@ -68,5 +77,6 @@ function readProviderConfig( projectKey: projectKeyPattern ? new RegExp(projectKeyPattern) : undefined, repoSlug: repoSlugPattern ? new RegExp(repoSlugPattern) : undefined, }, + schedule, }; } diff --git a/plugins/catalog-backend-module-bitbucket-server/src/service/BitbucketServerEntityProviderCatalogModule.test.ts b/plugins/catalog-backend-module-bitbucket-server/src/service/BitbucketServerEntityProviderCatalogModule.test.ts new file mode 100644 index 0000000000..d2ce4dbd3d --- /dev/null +++ b/plugins/catalog-backend-module-bitbucket-server/src/service/BitbucketServerEntityProviderCatalogModule.test.ts @@ -0,0 +1,91 @@ +/* + * Copyright 2022 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 { getVoidLogger } from '@backstage/backend-common'; +import { + configServiceRef, + loggerServiceRef, + schedulerServiceRef, +} from '@backstage/backend-plugin-api'; +import { + PluginTaskScheduler, + TaskScheduleDefinition, +} from '@backstage/backend-tasks'; +import { startTestBackend } from '@backstage/backend-test-utils'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node'; +import { bitbucketServerEntityProviderCatalogModule } from './BitbucketServerEntityProviderCatalogModule'; +import { Duration } from 'luxon'; +import { BitbucketServerEntityProvider } from '../providers'; + +describe('bitbucketServerEntityProviderCatalogModule', () => { + it('should register provider at the catalog extension point', async () => { + let addedProviders: Array | undefined; + let usedSchedule: TaskScheduleDefinition | undefined; + + const extensionPoint = { + addEntityProvider: (providers: any) => { + addedProviders = providers; + }, + }; + const runner = jest.fn(); + const scheduler = { + createScheduledTaskRunner: (schedule: TaskScheduleDefinition) => { + usedSchedule = schedule; + return runner; + }, + } as unknown as PluginTaskScheduler; + + const config = new ConfigReader({ + catalog: { + providers: { + bitbucketServer: { + host: 'bitbucket.mycompany.com', + schedule: { + frequency: 'P1M', + timeout: 'PT3M', + }, + }, + }, + }, + integrations: { + bitbucketServer: [ + { + host: 'bitbucket.mycompany.com', + }, + ], + }, + }); + + await startTestBackend({ + extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], + services: [ + [configServiceRef, config], + [loggerServiceRef, getVoidLogger()], + [schedulerServiceRef, scheduler], + ], + features: [bitbucketServerEntityProviderCatalogModule()], + }); + + expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M')); + expect(usedSchedule?.timeout).toEqual(Duration.fromISO('PT3M')); + expect(addedProviders?.length).toEqual(1); + expect(addedProviders?.pop()?.getProviderName()).toEqual( + 'bitbucketServer-provider:default', + ); + expect(runner).not.toHaveBeenCalled(); + }); +}); diff --git a/plugins/catalog-backend-module-bitbucket-server/src/service/BitbucketServerEntityProviderCatalogModule.ts b/plugins/catalog-backend-module-bitbucket-server/src/service/BitbucketServerEntityProviderCatalogModule.ts new file mode 100644 index 0000000000..e89eee7b45 --- /dev/null +++ b/plugins/catalog-backend-module-bitbucket-server/src/service/BitbucketServerEntityProviderCatalogModule.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2022 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 { + configServiceRef, + createBackendModule, + loggerServiceRef, + loggerToWinstonLogger, + schedulerServiceRef, +} from '@backstage/backend-plugin-api'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node'; +import { BitbucketServerEntityProvider } from '../providers'; + +/** + * @alpha + */ +export const bitbucketServerEntityProviderCatalogModule = createBackendModule({ + pluginId: 'catalog', + moduleId: 'bitbucketServerEntityProvider', + register(env) { + env.registerInit({ + deps: { + catalog: catalogProcessingExtensionPoint, + config: configServiceRef, + logger: loggerServiceRef, + scheduler: schedulerServiceRef, + }, + async init({ catalog, config, logger, scheduler }) { + const winstonLogger = loggerToWinstonLogger(logger); + const providers = BitbucketServerEntityProvider.fromConfig(config, { + logger: winstonLogger, + scheduler, + }); + + catalog.addEntityProvider(providers); + }, + }); + }, +}); diff --git a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md index eb9eb97acc..ebc6e7b9df 100644 --- a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/plugin-catalog-backend-module-bitbucket +## 0.2.4 + +### Patch Changes + +- 23f9199a0f: Deprecate `@backstage/plugin-catalog-backend-module-bitbucket`. + + Please migrate to `@backstage/plugin-catalog-backend-module-bitbucket-cloud` + or `@backstage/plugin-catalog-backend-module-bitbucket-server` instead. + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.0 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## 0.2.4-next.2 + +### Patch Changes + +- 23f9199a0f: Deprecate `@backstage/plugin-catalog-backend-module-bitbucket`. + + Please migrate to `@backstage/plugin-catalog-backend-module-bitbucket-cloud` + or `@backstage/plugin-catalog-backend-module-bitbucket-server` instead. + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.0-next.2 + ## 0.2.4-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket/api-report.md b/plugins/catalog-backend-module-bitbucket/api-report.md index 34acd6e76d..053c6040a4 100644 --- a/plugins/catalog-backend-module-bitbucket/api-report.md +++ b/plugins/catalog-backend-module-bitbucket/api-report.md @@ -12,7 +12,7 @@ import { LocationSpec } from '@backstage/plugin-catalog-backend'; import { Logger } from 'winston'; import { ScmIntegrationRegistry } from '@backstage/integration'; -// @public (undocumented) +// @public @deprecated (undocumented) export class BitbucketDiscoveryProcessor implements CatalogProcessor { constructor(options: { integrations: ScmIntegrationRegistry; @@ -37,7 +37,7 @@ export class BitbucketDiscoveryProcessor implements CatalogProcessor { ): Promise; } -// @public +// @public @deprecated export type BitbucketRepositoryParser = (options: { integration: BitbucketIntegration; target: string; diff --git a/plugins/catalog-backend-module-bitbucket/package.json b/plugins/catalog-backend-module-bitbucket/package.json index ff0594154d..2d4eae3b8f 100644 --- a/plugins/catalog-backend-module-bitbucket/package.json +++ b/plugins/catalog-backend-module-bitbucket/package.json @@ -1,7 +1,8 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket", - "version": "0.2.4-next.1", + "version": "0.2.4", + "deprecated": true, "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket/src/BitbucketDiscoveryProcessor.ts b/plugins/catalog-backend-module-bitbucket/src/BitbucketDiscoveryProcessor.ts index 14bf90ee62..6924765483 100644 --- a/plugins/catalog-backend-module-bitbucket/src/BitbucketDiscoveryProcessor.ts +++ b/plugins/catalog-backend-module-bitbucket/src/BitbucketDiscoveryProcessor.ts @@ -41,7 +41,10 @@ import { const DEFAULT_BRANCH = 'master'; const DEFAULT_CATALOG_LOCATION = '/catalog-info.yaml'; -/** @public */ +/** + * @public + * @deprecated Please migrate to `@backstage/plugin-catalog-backend-module-bitbucket-cloud` or `@backstage/plugin-catalog-backend-module-bitbucket-server` instead. + */ export class BitbucketDiscoveryProcessor implements CatalogProcessor { private readonly integrations: ScmIntegrationRegistry; private readonly parser: BitbucketRepositoryParser; @@ -70,6 +73,9 @@ export class BitbucketDiscoveryProcessor implements CatalogProcessor { this.integrations = options.integrations; this.parser = options.parser || defaultRepositoryParser; this.logger = options.logger; + this.logger.warn( + 'Please migrate to `@backstage/plugin-catalog-backend-module-bitbucket-cloud` or `@backstage/plugin-catalog-backend-module-bitbucket-server` instead.', + ); } getProcessorName(): string { diff --git a/plugins/catalog-backend-module-bitbucket/src/lib/BitbucketRepositoryParser.ts b/plugins/catalog-backend-module-bitbucket/src/lib/BitbucketRepositoryParser.ts index 57a162e1e4..97225e2579 100644 --- a/plugins/catalog-backend-module-bitbucket/src/lib/BitbucketRepositoryParser.ts +++ b/plugins/catalog-backend-module-bitbucket/src/lib/BitbucketRepositoryParser.ts @@ -26,6 +26,7 @@ import { Logger } from 'winston'; * results. * * @public + * @deprecated Please migrate to `@backstage/plugin-catalog-backend-module-bitbucket-cloud` or `@backstage/plugin-catalog-backend-module-bitbucket-server` instead. */ export type BitbucketRepositoryParser = (options: { integration: BitbucketIntegration; diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index 8e1b11c1f7..e5ba22ced6 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + +## 0.1.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + ## 0.1.5-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/api-report.md b/plugins/catalog-backend-module-gerrit/api-report.md index 7eae94c2a8..45847ea93d 100644 --- a/plugins/catalog-backend-module-gerrit/api-report.md +++ b/plugins/catalog-backend-module-gerrit/api-report.md @@ -3,10 +3,12 @@ > 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 { Config } from '@backstage/config'; import { EntityProvider } from '@backstage/plugin-catalog-backend'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; import { Logger } from 'winston'; +import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { TaskRunner } from '@backstage/backend-tasks'; // @public (undocumented) @@ -18,7 +20,8 @@ export class GerritEntityProvider implements EntityProvider { configRoot: Config, options: { logger: Logger; - schedule: TaskRunner; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; }, ): GerritEntityProvider[]; // (undocumented) @@ -27,5 +30,10 @@ export class GerritEntityProvider implements EntityProvider { refresh(logger: Logger): Promise; } +// @alpha (undocumented) +export const gerritEntityProviderCatalogModule: ( + options?: undefined, +) => BackendFeature; + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index e3665a96ae..9b57cf949b 100644 --- a/plugins/catalog-backend-module-gerrit/package.json +++ b/plugins/catalog-backend-module-gerrit/package.json @@ -1,11 +1,12 @@ { "name": "@backstage/plugin-catalog-backend-module-gerrit", - "version": "0.1.5-next.1", + "version": "0.1.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { "access": "public", + "alphaTypes": "dist/index.alpha.d.ts", "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, @@ -20,21 +21,23 @@ }, "scripts": { "start": "backstage-cli package start", - "build": "backstage-cli package build", + "build": "backstage-cli package build --experimental-type-build", "lint": "backstage-cli package lint", "test": "backstage-cli package test", - "clean": "backstage-cli package clean", "prepack": "backstage-cli package prepack", - "postpack": "backstage-cli package postpack" + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean" }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/backend-tasks": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", "fs-extra": "10.1.0", "msw": "^0.47.0", "node-fetch": "^2.6.7", @@ -44,7 +47,8 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", - "@types/fs-extra": "^9.0.1" + "@types/fs-extra": "^9.0.1", + "luxon": "^3.0.0" }, "files": [ "dist", diff --git a/plugins/catalog-backend-module-gerrit/src/index.ts b/plugins/catalog-backend-module-gerrit/src/index.ts index 133772d4b1..38ede69926 100644 --- a/plugins/catalog-backend-module-gerrit/src/index.ts +++ b/plugins/catalog-backend-module-gerrit/src/index.ts @@ -15,3 +15,4 @@ */ export { GerritEntityProvider } from './providers/GerritEntityProvider'; +export { gerritEntityProviderCatalogModule } from './service/GerritEntityProviderCatalogModule'; diff --git a/plugins/catalog-backend-module-gerrit/src/providers/GerritEntityProvider.test.ts b/plugins/catalog-backend-module-gerrit/src/providers/GerritEntityProvider.test.ts index d6ad0ff668..5d258ee94f 100644 --- a/plugins/catalog-backend-module-gerrit/src/providers/GerritEntityProvider.test.ts +++ b/plugins/catalog-backend-module-gerrit/src/providers/GerritEntityProvider.test.ts @@ -15,14 +15,18 @@ */ import { getVoidLogger } from '@backstage/backend-common'; -import { ConfigReader } from '@backstage/config'; -import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks'; +import { + PluginTaskScheduler, + TaskInvocationDefinition, + TaskRunner, +} from '@backstage/backend-tasks'; import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { ConfigReader } from '@backstage/config'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; -import { rest } from 'msw'; import fs from 'fs-extra'; -import path from 'path'; +import { rest } from 'msw'; import { setupServer } from 'msw/node'; +import path from 'path'; import { GerritEntityProvider } from './GerritEntityProvider'; const server = setupServer(); @@ -207,4 +211,93 @@ describe('GerritEntityProvider', () => { }), ).toThrow(/No gerrit integration/); }); + + it('fail without schedule and scheduler', () => { + expect(() => + GerritEntityProvider.fromConfig(config, { + logger, + }), + ).toThrow('Either schedule or scheduler must be provided'); + }); + + it('fail with scheduler but no schedule config', () => { + const scheduler = { + createScheduledTaskRunner: (_: any) => jest.fn(), + } as unknown as PluginTaskScheduler; + expect(() => + GerritEntityProvider.fromConfig(config, { + logger, + scheduler, + }), + ).toThrow( + 'No schedule provided neither via code nor config for gerrit-provider:active-training', + ); + }); + + it('discovers projects from the api with schedule in config', async () => { + const configWithSchedule = new ConfigReader({ + catalog: { + providers: { + gerrit: { + 'active-training': { + host: 'g.com', + query: 'state=ACTIVE&prefix=training', + branch: 'main', + schedule: { + frequency: 'PT30M', + timeout: { + minutes: 3, + }, + }, + }, + }, + }, + }, + integrations: { + gerrit: [ + { + host: 'g.com', + baseUrl: 'https://g.com/gerrit', + gitilesBaseUrl: 'https:/g.com/gitiles', + }, + ], + }, + }); + const scheduler = { + createScheduledTaskRunner: (_: any) => schedule, + } as unknown as PluginTaskScheduler; + + const repoBuffer = fs.readFileSync( + path.resolve(__dirname, '__fixtures__/listProjectsBody.txt'), + ); + const expected = getJsonFixture('expectedProviderEntities.json'); + + server.use( + rest.get('https://g.com/gerrit/projects/', (_, res, ctx) => + res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.body(repoBuffer), + ), + ), + ); + + const provider = GerritEntityProvider.fromConfig(configWithSchedule, { + logger, + scheduler, + })[0]; + expect(provider.getProviderName()).toEqual( + 'gerrit-provider:active-training', + ); + + await provider.connect(entityProviderConnection); + + const taskDef = schedule.getTasks()[0]; + expect(taskDef.id).toEqual('gerrit-provider:active-training:refresh'); + await (taskDef.fn as () => Promise)(); + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith( + expected, + ); + }); }); diff --git a/plugins/catalog-backend-module-gerrit/src/providers/GerritEntityProvider.ts b/plugins/catalog-backend-module-gerrit/src/providers/GerritEntityProvider.ts index beb4052073..ec08dbe4be 100644 --- a/plugins/catalog-backend-module-gerrit/src/providers/GerritEntityProvider.ts +++ b/plugins/catalog-backend-module-gerrit/src/providers/GerritEntityProvider.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { TaskRunner } from '@backstage/backend-tasks'; +import { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks'; import { Config } from '@backstage/config'; import { InputError } from '@backstage/errors'; import { @@ -49,9 +49,14 @@ export class GerritEntityProvider implements EntityProvider { configRoot: Config, options: { logger: Logger; - schedule: TaskRunner; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; }, ): GerritEntityProvider[] { + if (!options.schedule && !options.scheduler) { + throw new Error('Either schedule or scheduler must be provided.'); + } + const providerConfigs = readGerritConfigs(configRoot); const integrations = ScmIntegrations.fromConfig(configRoot).gerrit; const providers: GerritEntityProvider[] = []; @@ -63,12 +68,23 @@ export class GerritEntityProvider implements EntityProvider { `No gerrit integration found that matches host ${providerConfig.host}`, ); } + + if (!options.schedule && !providerConfig.schedule) { + throw new Error( + `No schedule provided neither via code nor config for gerrit-provider:${providerConfig.id}.`, + ); + } + + const taskRunner = + options.schedule ?? + options.scheduler!.createScheduledTaskRunner(providerConfig.schedule!); + providers.push( new GerritEntityProvider( providerConfig, integration, options.logger, - options.schedule, + taskRunner, ), ); }); @@ -79,14 +95,14 @@ export class GerritEntityProvider implements EntityProvider { config: GerritProviderConfig, integration: GerritIntegration, logger: Logger, - schedule: TaskRunner, + taskRunner: TaskRunner, ) { this.config = config; this.integration = integration; this.logger = logger.child({ target: this.getProviderName(), }); - this.scheduleFn = this.createScheduleFn(schedule); + this.scheduleFn = this.createScheduleFn(taskRunner); } getProviderName(): string { @@ -98,10 +114,10 @@ export class GerritEntityProvider implements EntityProvider { await this.scheduleFn(); } - private createScheduleFn(schedule: TaskRunner): () => Promise { + private createScheduleFn(taskRunner: TaskRunner): () => Promise { return async () => { const taskId = `${this.getProviderName()}:refresh`; - return schedule.run({ + return taskRunner.run({ id: taskId, fn: async () => { const logger = this.logger.child({ diff --git a/plugins/catalog-backend-module-gerrit/src/providers/config.test.ts b/plugins/catalog-backend-module-gerrit/src/providers/config.test.ts index 0422b4e4ea..ecf68d9f3c 100644 --- a/plugins/catalog-backend-module-gerrit/src/providers/config.test.ts +++ b/plugins/catalog-backend-module-gerrit/src/providers/config.test.ts @@ -15,6 +15,7 @@ */ import { ConfigReader } from '@backstage/config'; +import { Duration } from 'luxon'; import { readGerritConfigs } from './config'; describe('readGerritConfigs', () => { @@ -29,12 +30,24 @@ describe('readGerritConfigs', () => { query: 'state=ACTIVE', branch: 'main', }; + const provider3 = { + host: 'gerrit1.com', + query: 'state=ACTIVE', + branch: 'main', + schedule: { + frequency: 'PT30M', + timeout: { + minutes: 3, + }, + }, + }; const config = { catalog: { providers: { gerrit: { 'active-g1': provider1, 'active-g2': provider2, + 'active-g3': provider3, }, }, }, @@ -42,10 +55,19 @@ describe('readGerritConfigs', () => { const actual = readGerritConfigs(new ConfigReader(config)); - expect(actual).toHaveLength(2); + expect(actual).toHaveLength(3); expect(actual[0]).toEqual({ ...provider1, id: 'active-g1' }); expect(actual[1]).toEqual({ ...provider2, id: 'active-g2' }); + expect(actual[2]).toEqual({ + ...provider3, + id: 'active-g3', + schedule: { + ...provider3.schedule, + frequency: Duration.fromISO(provider3.schedule.frequency), + }, + }); }); + it('provides default values', () => { const provider = { host: 'gerrit1.com', diff --git a/plugins/catalog-backend-module-gerrit/src/providers/config.ts b/plugins/catalog-backend-module-gerrit/src/providers/config.ts index 7a28ddd8ea..dd7ee9ec43 100644 --- a/plugins/catalog-backend-module-gerrit/src/providers/config.ts +++ b/plugins/catalog-backend-module-gerrit/src/providers/config.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { readTaskScheduleDefinitionFromConfig } from '@backstage/backend-tasks'; import { Config } from '@backstage/config'; import { GerritProviderConfig } from './types'; @@ -22,11 +23,16 @@ function readGerritConfig(id: string, config: Config): GerritProviderConfig { const host = config.getString('host'); const query = config.getString('query'); + const schedule = config.has('schedule') + ? readTaskScheduleDefinitionFromConfig(config.getConfig('schedule')) + : undefined; + return { branch, host, id, query, + schedule, }; } diff --git a/plugins/catalog-backend-module-gerrit/src/providers/types.ts b/plugins/catalog-backend-module-gerrit/src/providers/types.ts index 1e96bd1ee4..4a8329df86 100644 --- a/plugins/catalog-backend-module-gerrit/src/providers/types.ts +++ b/plugins/catalog-backend-module-gerrit/src/providers/types.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { TaskScheduleDefinition } from '@backstage/backend-tasks'; + export type GerritProjectInfo = { id: string; name: string; @@ -28,4 +30,5 @@ export type GerritProviderConfig = { query: string; id: string; branch?: string; + schedule?: TaskScheduleDefinition; }; diff --git a/plugins/catalog-backend-module-gerrit/src/service/GerritEntityProviderCatalogModule.test.ts b/plugins/catalog-backend-module-gerrit/src/service/GerritEntityProviderCatalogModule.test.ts new file mode 100644 index 0000000000..53e012209b --- /dev/null +++ b/plugins/catalog-backend-module-gerrit/src/service/GerritEntityProviderCatalogModule.test.ts @@ -0,0 +1,97 @@ +/* + * Copyright 2022 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 { getVoidLogger } from '@backstage/backend-common'; +import { + configServiceRef, + loggerServiceRef, + schedulerServiceRef, +} from '@backstage/backend-plugin-api'; +import { + PluginTaskScheduler, + TaskScheduleDefinition, +} from '@backstage/backend-tasks'; +import { startTestBackend } from '@backstage/backend-test-utils'; +import { ConfigReader } from '@backstage/config'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node'; +import { Duration } from 'luxon'; +import { gerritEntityProviderCatalogModule } from './GerritEntityProviderCatalogModule'; +import { GerritEntityProvider } from '../providers/GerritEntityProvider'; + +describe('gerritEntityProviderCatalogModule', () => { + it('should register provider at the catalog extension point', async () => { + let addedProviders: Array | undefined; + let usedSchedule: TaskScheduleDefinition | undefined; + + const extensionPoint = { + addEntityProvider: (providers: any) => { + addedProviders = providers; + }, + }; + const runner = jest.fn(); + const scheduler = { + createScheduledTaskRunner: (schedule: TaskScheduleDefinition) => { + usedSchedule = schedule; + return runner; + }, + } as unknown as PluginTaskScheduler; + + const config = new ConfigReader({ + catalog: { + providers: { + gerrit: { + test: { + host: 'g.com', + query: 'state=ACTIVE&prefix=training', + branch: 'main', + schedule: { + frequency: 'P1M', + timeout: 'PT3M', + }, + }, + }, + }, + }, + integrations: { + gerrit: [ + { + host: 'g.com', + baseUrl: 'https://g.com/gerrit', + gitilesBaseUrl: 'https:/g.com/gitiles', + }, + ], + }, + }); + + await startTestBackend({ + extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], + services: [ + [configServiceRef, config], + [loggerServiceRef, getVoidLogger()], + [schedulerServiceRef, scheduler], + ], + features: [gerritEntityProviderCatalogModule()], + }); + + expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M')); + expect(usedSchedule?.timeout).toEqual(Duration.fromISO('PT3M')); + expect(addedProviders?.length).toEqual(1); + expect(addedProviders?.pop()?.getProviderName()).toEqual( + 'gerrit-provider:test', + ); + expect(runner).not.toHaveBeenCalled(); + }); +}); diff --git a/plugins/catalog-backend-module-gerrit/src/service/GerritEntityProviderCatalogModule.ts b/plugins/catalog-backend-module-gerrit/src/service/GerritEntityProviderCatalogModule.ts new file mode 100644 index 0000000000..32d582e397 --- /dev/null +++ b/plugins/catalog-backend-module-gerrit/src/service/GerritEntityProviderCatalogModule.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2022 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 { + configServiceRef, + createBackendModule, + loggerServiceRef, + loggerToWinstonLogger, + schedulerServiceRef, +} from '@backstage/backend-plugin-api'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node'; +import { GerritEntityProvider } from '../providers/GerritEntityProvider'; + +/** + * @alpha + */ +export const gerritEntityProviderCatalogModule = createBackendModule({ + pluginId: 'catalog', + moduleId: 'gerritEntityProvider', + register(env) { + env.registerInit({ + deps: { + catalog: catalogProcessingExtensionPoint, + config: configServiceRef, + logger: loggerServiceRef, + scheduler: schedulerServiceRef, + }, + async init({ catalog, config, logger, scheduler }) { + const winstonLogger = loggerToWinstonLogger(logger); + const providers = GerritEntityProvider.fromConfig(config, { + logger: winstonLogger, + scheduler, + }); + + catalog.addEntityProvider(providers); + }, + }); + }, +}); diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index 1a7a4a512c..9c89e00de4 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,70 @@ # @backstage/plugin-catalog-backend-module-github +## 0.1.8 + +### Patch Changes + +- 8749df3d02: `GitHubEntityProvider`: Add option to configure schedule via `app-config.yaml` instead of in code. + + Please find how to configure the schedule at the config at + https://backstage.io/docs/integrations/github/discovery + +- 7022aebf35: Added `GithubLocationAnalyzer`. This can be used to add to the `CatalogBuilder`. When added this will be used by `RepoLocationAnalyzer` to figure out if the given URL that you are trying to import from the /catalog-import page already contains catalog-info.yaml files. +- 51046b58b0: Use schedule from config at backend module. + + Also, it removes `GithubEntityProviderCatalogModuleOptions` + in favor of config-only for the backend module setup + like at other similar modules. + +- 7edb5909e8: Add missing config schema for the `GitHubEntityProvider`. +- be9474b103: Replaces in-code uses of `GitHub` by `Github` and deprecates old versions. + + Deprecates + + - `GitHubEntityProvider` replaced by `GithubEntityProvider` + - `GitHubLocationAnalyzer` replaced by `GithubLocationAnalyzer` + - `GitHubLocationAnalyzerOptions` replaced by `GithubLocationAnalyzerOptions` + - `GitHubOrgEntityProvider` replaced by `GithubOrgEntityProvider` + - `GitHubOrgEntityProviderOptions` replaced by `GithubOrgEntityProviderOptions` + + Renames + + - `GitHubLocationAnalyzer` to `GithubLocationAnalyzer` + - `GitHubLocationAnalyzerOptions` to `GithubLocationAnalyzerOptions` + +- a35a27df70: Updated the `moduleId` of the experimental module export. +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0 + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/backend-plugin-api@0.1.3 + - @backstage/catalog-client@1.1.1 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## 0.1.8-next.2 + +### Patch Changes + +- 7022aebf35: Added `GitHubLocationAnalyzer`. This can be used to add to the `CatalogBuilder`. When added this will be used by `RepoLocationAnalyzer` to figure out if the given URL that you are trying to import from the /catalog-import page already contains catalog-info.yaml files. +- 7edb5909e8: Add missing config schema for the `GitHubEntityProvider`. +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0-next.2 + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + ## 0.1.8-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/api-report.md b/plugins/catalog-backend-module-github/api-report.md index 8e8055bafc..5b94f7169e 100644 --- a/plugins/catalog-backend-module-github/api-report.md +++ b/plugins/catalog-backend-module-github/api-report.md @@ -3,19 +3,23 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { AnalyzeOptions } from '@backstage/plugin-catalog-backend'; import { BackendFeature } from '@backstage/backend-plugin-api'; import { CatalogProcessor } from '@backstage/plugin-catalog-backend'; import { CatalogProcessorEmit } from '@backstage/plugin-catalog-backend'; import { Config } from '@backstage/config'; +import { Entity } from '@backstage/catalog-model'; import { EntityProvider } from '@backstage/plugin-catalog-backend'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; import { GithubCredentialsProvider } from '@backstage/integration'; import { GitHubIntegrationConfig } from '@backstage/integration'; import { LocationSpec } from '@backstage/plugin-catalog-backend'; import { Logger } from 'winston'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { ScmIntegrationRegistry } from '@backstage/integration'; +import { ScmLocationAnalyzer } from '@backstage/plugin-catalog-backend'; import { TaskRunner } from '@backstage/backend-tasks'; -import { TaskScheduleDefinition } from '@backstage/backend-tasks'; // @public export class GithubDiscoveryProcessor implements CatalogProcessor { @@ -42,7 +46,7 @@ export class GithubDiscoveryProcessor implements CatalogProcessor { ): Promise; } -// @public +// @public @deprecated (undocumented) export class GitHubEntityProvider implements EntityProvider { // (undocumented) connect(connection: EntityProviderConnection): Promise; @@ -51,7 +55,8 @@ export class GitHubEntityProvider implements EntityProvider { config: Config, options: { logger: Logger; - schedule: TaskRunner; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; }, ): GitHubEntityProvider[]; // (undocumented) @@ -60,14 +65,52 @@ export class GitHubEntityProvider implements EntityProvider { refresh(logger: Logger): Promise; } -// @alpha -export const githubEntityProviderCatalogModule: ( - options?: GithubEntityProviderCatalogModuleOptions | undefined, -) => BackendFeature; +// @public +export class GithubEntityProvider implements EntityProvider { + // (undocumented) + connect(connection: EntityProviderConnection): Promise; + // (undocumented) + static fromConfig( + config: Config, + options: { + logger: Logger; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; + }, + ): GithubEntityProvider[]; + // (undocumented) + getProviderName(): string; + // (undocumented) + refresh(logger: Logger): Promise; +} // @alpha -export type GithubEntityProviderCatalogModuleOptions = { - schedule?: TaskScheduleDefinition; +export const githubEntityProviderCatalogModule: ( + options?: undefined, +) => BackendFeature; + +// @public (undocumented) +export class GithubLocationAnalyzer implements ScmLocationAnalyzer { + constructor(options: GithubLocationAnalyzerOptions); + // (undocumented) + analyze({ url, catalogFilename }: AnalyzeOptions): Promise<{ + existing: { + location: { + type: string; + target: string; + }; + isRegistered: boolean; + entity: Entity; + }[]; + }>; + // (undocumented) + supports(url: string): boolean; +} + +// @public (undocumented) +export type GithubLocationAnalyzerOptions = { + config: Config; + discovery: PluginEndpointDiscovery; }; // @public @@ -103,8 +146,17 @@ export class GithubMultiOrgReaderProcessor implements CatalogProcessor { ): Promise; } +// @public @deprecated (undocumented) +export class GitHubOrgEntityProvider extends GithubOrgEntityProvider { + // (undocumented) + static fromConfig( + config: Config, + options: GitHubOrgEntityProviderOptions, + ): GitHubOrgEntityProvider; +} + // @public -export class GitHubOrgEntityProvider implements EntityProvider { +export class GithubOrgEntityProvider implements EntityProvider { constructor(options: { id: string; orgUrl: string; @@ -117,15 +169,18 @@ export class GitHubOrgEntityProvider implements EntityProvider { // (undocumented) static fromConfig( config: Config, - options: GitHubOrgEntityProviderOptions, - ): GitHubOrgEntityProvider; + options: GithubOrgEntityProviderOptions, + ): GithubOrgEntityProvider; // (undocumented) getProviderName(): string; read(options?: { logger?: Logger }): Promise; } +// @public @deprecated (undocumented) +export type GitHubOrgEntityProviderOptions = GithubOrgEntityProviderOptions; + // @public -export interface GitHubOrgEntityProviderOptions { +export interface GithubOrgEntityProviderOptions { githubCredentialsProvider?: GithubCredentialsProvider; id: string; logger: Logger; diff --git a/plugins/catalog-backend-module-github/config.d.ts b/plugins/catalog-backend-module-github/config.d.ts index 1dcd49ea4e..691caedc46 100644 --- a/plugins/catalog-backend-module-github/config.d.ts +++ b/plugins/catalog-backend-module-github/config.d.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { TaskScheduleDefinitionConfig } from '@backstage/backend-tasks'; + export interface Config { catalog?: { processors?: { @@ -48,7 +50,7 @@ export interface Config { providers?: { /** - * GitHubEntityProvider configuration + * GithubEntityProvider configuration * * Uses "default" as default id for the single config variant. */ @@ -103,6 +105,10 @@ export interface Config { exclude?: string[]; }; }; + /** + * (Optional) TaskScheduleDefinition for the refresh. + */ + schedule?: TaskScheduleDefinitionConfig; } | Record< string, @@ -156,6 +162,10 @@ export interface Config { exclude?: string[]; }; }; + /** + * (Optional) TaskScheduleDefinition for the refresh. + */ + schedule?: TaskScheduleDefinitionConfig; } >; }; diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index 686b45f13c..e1a3ed39b8 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-github", "description": "A Backstage catalog backend module that helps integrate towards GitHub", - "version": "0.1.8-next.1", + "version": "0.1.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -36,6 +36,7 @@ "@backstage/backend-common": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", @@ -44,6 +45,8 @@ "@backstage/plugin-catalog-node": "workspace:^", "@backstage/types": "workspace:^", "@octokit/graphql": "^5.0.0", + "@octokit/rest": "^19.0.3", + "git-url-parse": "^13.0.0", "lodash": "^4.17.21", "msw": "^0.47.0", "node-fetch": "^2.6.7", @@ -53,7 +56,8 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", - "@types/lodash": "^4.14.151" + "@types/lodash": "^4.14.151", + "luxon": "^3.0.0" }, "files": [ "dist", diff --git a/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.test.ts b/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.test.ts new file mode 100644 index 0000000000..936f8a3576 --- /dev/null +++ b/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.test.ts @@ -0,0 +1,156 @@ +/* + * Copyright 2022 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. + */ + +const octokit = { + search: { + code: jest.fn(), + }, + repos: { + get: jest.fn(), + }, +}; + +jest.mock('@octokit/rest', () => { + class Octokit { + constructor() { + return octokit; + } + } + return { Octokit }; +}); + +import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { GithubLocationAnalyzer } from './GithubLocationAnalyzer'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { setupServer } from 'msw/node'; +import { rest } from 'msw'; +import { ConfigReader } from '@backstage/config'; + +const server = setupServer(); + +describe('GithubLocationAnalyzer', () => { + const mockDiscoveryApi: jest.Mocked = { + getBaseUrl: jest.fn().mockResolvedValue('http://localhost:7007'), + getExternalBaseUrl: jest.fn(), + }; + const config = new ConfigReader({ + integrations: { + github: [ + { + host: 'h.com', + token: 't', + }, + ], + }, + }); + + setupRequestMockHandlers(server); + + beforeEach(() => { + server.use( + rest.post('http://localhost:7007/locations', async (_, res, ctx) => { + return res( + ctx.status(201), + ctx.json({ + location: 'test', + exists: false, + entities: [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Location', + metadata: { + name: 'test-entity', + }, + spec: { + type: 'url', + target: 'whatever', + }, + }, + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + title: 'Test Entity', + name: 'test-entity-2', + description: 'The expected description 2', + }, + spec: { + type: 'some-type', + lifecycle: 'experimental', + owner: 'someone', + }, + }, + ], + }), + ); + }), + ); + + octokit.repos.get.mockResolvedValue({ + data: { default_branch: 'my_default_branch' }, + }); + }); + + it('should analyze', async () => { + octokit.search.code.mockImplementation((opts: { q: string }) => { + if (opts.q === 'filename:catalog-info.yaml repo:foo/bar') { + return Promise.resolve({ + data: { items: [{ path: 'catalog-info.yaml' }], total_count: 1 }, + }); + } + return Promise.reject(); + }); + + const analyzer = new GithubLocationAnalyzer({ + discovery: mockDiscoveryApi, + config, + }); + const result = await analyzer.analyze({ + url: 'https://github.com/foo/bar', + }); + + expect(result.existing[0].isRegistered).toBeFalsy(); + expect(result.existing[0].location).toEqual({ + type: 'url', + target: + 'https://github.com/foo/bar/blob/my_default_branch/catalog-info.yaml', + }); + }); + it('should use the provided entity filename for search', async () => { + octokit.search.code.mockImplementation((opts: { q: string }) => { + if (opts.q === 'filename:anvil.yaml repo:foo/bar') { + return Promise.resolve({ + data: { items: [{ path: 'anvil.yaml' }], total_count: 1 }, + }); + } + return Promise.reject(); + }); + + const analyzer = new GithubLocationAnalyzer({ + discovery: mockDiscoveryApi, + config, + }); + const result = await analyzer.analyze({ + url: 'https://github.com/foo/bar', + catalogFilename: 'anvil.yaml', + }); + + expect(result.existing[0].location).toEqual({ + type: 'url', + target: 'https://github.com/foo/bar/blob/my_default_branch/anvil.yaml', + }); + }); +}); diff --git a/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts b/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts new file mode 100644 index 0000000000..32ef8bf946 --- /dev/null +++ b/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts @@ -0,0 +1,106 @@ +/* + * Copyright 2022 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 { CatalogApi, CatalogClient } from '@backstage/catalog-client'; +import { ScmIntegrations } from '@backstage/integration'; +import { Octokit } from '@octokit/rest'; +import { trimEnd } from 'lodash'; +import parseGitUrl from 'git-url-parse'; +import { + AnalyzeOptions, + ScmLocationAnalyzer, +} from '@backstage/plugin-catalog-backend'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { Config } from '@backstage/config'; + +/** @public */ +export type GithubLocationAnalyzerOptions = { + config: Config; + discovery: PluginEndpointDiscovery; +}; + +/** @public */ +export class GithubLocationAnalyzer implements ScmLocationAnalyzer { + private readonly catalogClient: CatalogApi; + private readonly config: Config; + + constructor(options: GithubLocationAnalyzerOptions) { + this.config = options.config; + this.catalogClient = new CatalogClient({ discoveryApi: options.discovery }); + } + + supports(url: string) { + const integrations = ScmIntegrations.fromConfig(this.config); + const integration = integrations.byUrl(url); + return integration?.type === 'github'; + } + + async analyze({ url, catalogFilename }: AnalyzeOptions) { + const { owner, name: repo } = parseGitUrl(url); + + const catalogFile = catalogFilename || 'catalog-info.yaml'; + + const query = `filename:${catalogFile} repo:${owner}/${repo}`; + + const integration = ScmIntegrations.fromConfig(this.config).github.byUrl( + url, + ); + if (!integration) { + throw new Error('Make sure you have a GitHub integration configured'); + } + + const octokitClient = new Octokit({ + auth: integration.config.token, + baseUrl: integration.config.apiBaseUrl, + }); + + const searchResult = await octokitClient.search + .code({ q: query }) + .catch(e => { + throw new Error(`Couldn't search repository for metadata file, ${e}`); + }); + + const exists = searchResult.data.total_count > 0; + if (exists) { + const repoInformation = await octokitClient.repos + .get({ owner, repo }) + .catch(e => { + throw new Error(`Couldn't fetch repo data, ${e}`); + }); + const defaultBranch = repoInformation.data.default_branch; + + const result = await Promise.all( + searchResult.data.items + .map(i => `${trimEnd(url, '/')}/blob/${defaultBranch}/${i.path}`) + .map(async target => { + const addLocationResult = await this.catalogClient.addLocation({ + type: 'url', + target, + dryRun: true, + }); + return addLocationResult.entities.map(e => ({ + location: { type: 'url', target }, + isRegistered: !!addLocationResult.exists, + entity: e, + })); + }), + ); + + return { existing: result.flat() }; + } + return { existing: [] }; + } +} diff --git a/plugins/catalog-backend-module-github/src/deprecated.ts b/plugins/catalog-backend-module-github/src/deprecated.ts new file mode 100644 index 0000000000..d094bb4031 --- /dev/null +++ b/plugins/catalog-backend-module-github/src/deprecated.ts @@ -0,0 +1,86 @@ +/* + * 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 { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks'; +import { Config } from '@backstage/config'; +import { + EntityProvider, + EntityProviderConnection, +} from '@backstage/plugin-catalog-backend'; +import { Logger } from 'winston'; +import { GithubEntityProvider } from './providers/GithubEntityProvider'; +import { + GithubOrgEntityProvider, + GithubOrgEntityProviderOptions, +} from './providers/GithubOrgEntityProvider'; + +/** + * @public + * @deprecated Use {@link GithubOrgEntityProvider} instead. + */ +export class GitHubOrgEntityProvider extends GithubOrgEntityProvider { + static fromConfig(config: Config, options: GitHubOrgEntityProviderOptions) { + options.logger.warn( + '[Deprecated] Use GithubOrgEntityProvider instead of GitHubOrgEntityProvider.', + ); + return GithubOrgEntityProvider.fromConfig( + config, + options as GithubOrgEntityProviderOptions, + ) as GitHubOrgEntityProvider; + } +} + +/** + * @public + * @deprecated Use {@link GithubOrgEntityProviderOptions} instead. + */ +export type GitHubOrgEntityProviderOptions = GithubOrgEntityProviderOptions; + +/** + * @public + * @deprecated Use {@link GithubEntityProvider} instead. + */ +export class GitHubEntityProvider implements EntityProvider { + static fromConfig( + config: Config, + options: { + logger: Logger; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; + }, + ): GitHubEntityProvider[] { + options.logger.warn( + '[Deprecated] Please use GithubEntityProvider instead of GitHubEntityProvider.', + ); + return GithubEntityProvider.fromConfig(config, options).map( + delegate => new GitHubEntityProvider(delegate), + ); + } + + private constructor(private readonly delegate: GithubEntityProvider) {} + + connect(connection: EntityProviderConnection): Promise { + return this.delegate.connect(connection); + } + + getProviderName(): string { + return this.delegate.getProviderName(); + } + + refresh(logger: Logger): Promise { + return this.delegate.refresh(logger); + } +} diff --git a/plugins/catalog-backend-module-github/src/index.ts b/plugins/catalog-backend-module-github/src/index.ts index d793d48f1e..f38035db15 100644 --- a/plugins/catalog-backend-module-github/src/index.ts +++ b/plugins/catalog-backend-module-github/src/index.ts @@ -20,12 +20,15 @@ * @packageDocumentation */ +export { GithubLocationAnalyzer } from './analyzers/GithubLocationAnalyzer'; +export type { GithubLocationAnalyzerOptions } from './analyzers/GithubLocationAnalyzer'; +export type { GithubMultiOrgConfig } from './lib'; export { GithubDiscoveryProcessor } from './processors/GithubDiscoveryProcessor'; export { GithubMultiOrgReaderProcessor } from './processors/GithubMultiOrgReaderProcessor'; export { GithubOrgReaderProcessor } from './processors/GithubOrgReaderProcessor'; -export { GitHubEntityProvider } from './providers/GitHubEntityProvider'; -export { GitHubOrgEntityProvider } from './providers/GitHubOrgEntityProvider'; -export type { GitHubOrgEntityProviderOptions } from './providers/GitHubOrgEntityProvider'; -export type { GithubMultiOrgConfig } from './lib'; -export { githubEntityProviderCatalogModule } from './module'; -export type { GithubEntityProviderCatalogModuleOptions } from './module'; +export { GithubEntityProvider } from './providers/GithubEntityProvider'; +export { GithubOrgEntityProvider } from './providers/GithubOrgEntityProvider'; +export type { GithubOrgEntityProviderOptions } from './providers/GithubOrgEntityProvider'; +export { githubEntityProviderCatalogModule } from './service/GithubEntityProviderCatalogModule'; + +export * from './deprecated'; diff --git a/plugins/catalog-backend-module-github/src/lib/index.ts b/plugins/catalog-backend-module-github/src/lib/index.ts index 26ec3e8516..6402b69121 100644 --- a/plugins/catalog-backend-module-github/src/lib/index.ts +++ b/plugins/catalog-backend-module-github/src/lib/index.ts @@ -22,4 +22,4 @@ export { getOrganizationUsers, } from './github'; export { assignGroupsToUsers, buildOrgHierarchy } from './org'; -export { parseGitHubOrgUrl } from './util'; +export { parseGithubOrgUrl } from './util'; diff --git a/plugins/catalog-backend-module-github/src/lib/util.test.ts b/plugins/catalog-backend-module-github/src/lib/util.test.ts index c530d2bcc9..7aca6839ea 100644 --- a/plugins/catalog-backend-module-github/src/lib/util.test.ts +++ b/plugins/catalog-backend-module-github/src/lib/util.test.ts @@ -14,17 +14,17 @@ * limitations under the License. */ -import { GithubTopicFilters } from '../providers/GitHubEntityProviderConfig'; -import { parseGitHubOrgUrl, satisfiesTopicFilter } from './util'; +import { GithubTopicFilters } from '../providers/GithubEntityProviderConfig'; +import { parseGithubOrgUrl, satisfiesTopicFilter } from './util'; -describe('parseGitHubOrgUrl', () => { +describe('parseGithubOrgUrl', () => { it('only supports clean org urls, and decodes them', () => { - expect(() => parseGitHubOrgUrl('https://github.com')).toThrow(); - expect(() => parseGitHubOrgUrl('https://github.com/org/foo')).toThrow(); + expect(() => parseGithubOrgUrl('https://github.com')).toThrow(); + expect(() => parseGithubOrgUrl('https://github.com/org/foo')).toThrow(); expect(() => - parseGitHubOrgUrl('https://github.com/org/foo/teams'), + parseGithubOrgUrl('https://github.com/org/foo/teams'), ).toThrow(); - expect(parseGitHubOrgUrl('https://github.com/foo%32')).toEqual({ + expect(parseGithubOrgUrl('https://github.com/foo%32')).toEqual({ org: 'foo2', }); }); diff --git a/plugins/catalog-backend-module-github/src/lib/util.ts b/plugins/catalog-backend-module-github/src/lib/util.ts index 238ed9868a..ece463bb40 100644 --- a/plugins/catalog-backend-module-github/src/lib/util.ts +++ b/plugins/catalog-backend-module-github/src/lib/util.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { GithubTopicFilters } from '../providers/GitHubEntityProviderConfig'; +import { GithubTopicFilters } from '../providers/GithubEntityProviderConfig'; -export function parseGitHubOrgUrl(urlString: string): { org: string } { +export function parseGithubOrgUrl(urlString: string): { org: string } { const path = new URL(urlString).pathname.substr(1).split('/'); // /backstage diff --git a/plugins/catalog-backend-module-github/src/processors/GithubOrgReaderProcessor.ts b/plugins/catalog-backend-module-github/src/processors/GithubOrgReaderProcessor.ts index c6f2f5d0e2..0ef81ac907 100644 --- a/plugins/catalog-backend-module-github/src/processors/GithubOrgReaderProcessor.ts +++ b/plugins/catalog-backend-module-github/src/processors/GithubOrgReaderProcessor.ts @@ -35,7 +35,7 @@ import { buildOrgHierarchy, getOrganizationTeams, getOrganizationUsers, - parseGitHubOrgUrl, + parseGithubOrgUrl, } from '../lib'; type GraphQL = typeof graphql; @@ -45,7 +45,7 @@ type GraphQL = typeof graphql; * * @remarks * - * Consider using {@link GitHubOrgEntityProvider} instead. + * Consider using {@link GithubOrgEntityProvider} instead. * * @public */ @@ -94,7 +94,7 @@ export class GithubOrgReaderProcessor implements CatalogProcessor { } const { client, tokenType } = await this.createClient(location.target); - const { org } = parseGitHubOrgUrl(location.target); + const { org } = parseGithubOrgUrl(location.target); // Read out all of the raw data const startTimestamp = Date.now(); diff --git a/plugins/catalog-backend-module-github/src/providers/GitHubEntityProvider.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.test.ts similarity index 77% rename from plugins/catalog-backend-module-github/src/providers/GitHubEntityProvider.test.ts rename to plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.test.ts index 8b315a8a1e..3dfea8ed67 100644 --- a/plugins/catalog-backend-module-github/src/providers/GitHubEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.test.ts @@ -15,10 +15,14 @@ */ import { getVoidLogger } from '@backstage/backend-common'; -import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks'; +import { + PluginTaskScheduler, + TaskInvocationDefinition, + TaskRunner, +} from '@backstage/backend-tasks'; import { ConfigReader } from '@backstage/config'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; -import { GitHubEntityProvider } from './GitHubEntityProvider'; +import { GithubEntityProvider } from './GithubEntityProvider'; import * as helpers from '../lib/github'; jest.mock('../lib/github', () => { @@ -41,13 +45,13 @@ class PersistingTaskRunner implements TaskRunner { const logger = getVoidLogger(); -describe('GitHubEntityProvider', () => { +describe('GithubEntityProvider', () => { afterEach(() => jest.resetAllMocks()); it('no provider config', () => { const schedule = new PersistingTaskRunner(); const config = new ConfigReader({}); - const providers = GitHubEntityProvider.fromConfig(config, { + const providers = GithubEntityProvider.fromConfig(config, { logger, schedule, }); @@ -66,7 +70,7 @@ describe('GitHubEntityProvider', () => { }, }, }); - const providers = GitHubEntityProvider.fromConfig(config, { + const providers = GithubEntityProvider.fromConfig(config, { logger, schedule, }); @@ -89,7 +93,7 @@ describe('GitHubEntityProvider', () => { }); expect(() => - GitHubEntityProvider.fromConfig(config, { + GithubEntityProvider.fromConfig(config, { logger, schedule, }), @@ -112,7 +116,7 @@ describe('GitHubEntityProvider', () => { }, }, }); - const providers = GitHubEntityProvider.fromConfig(config, { + const providers = GithubEntityProvider.fromConfig(config, { logger, schedule, }); @@ -149,7 +153,7 @@ describe('GitHubEntityProvider', () => { refresh: jest.fn(), }; - const provider = GitHubEntityProvider.fromConfig(config, { + const provider = GithubEntityProvider.fromConfig(config, { logger, schedule, })[0]; @@ -242,7 +246,7 @@ describe('GitHubEntityProvider', () => { refresh: jest.fn(), }; - const provider = GitHubEntityProvider.fromConfig(config, { + const provider = GithubEntityProvider.fromConfig(config, { logger, schedule, })[0]; @@ -321,7 +325,7 @@ describe('GitHubEntityProvider', () => { refresh: jest.fn(), }; - const provider = GitHubEntityProvider.fromConfig(config, { + const provider = GithubEntityProvider.fromConfig(config, { logger, schedule, })[0]; @@ -500,44 +504,43 @@ describe('GitHubEntityProvider', () => { entities: expectedEntities, }); }); -}); -it('apply full update on scheduled execution with topic exclusion taking priority over topic inclusion', async () => { - const config = new ConfigReader({ - catalog: { - providers: { - github: { - myProvider: { - organization: 'test-org', - catalogPath: 'custom/path/catalog-custom.yaml', - filters: { - branch: 'main', - repository: 'test-.*', - topic: { - exclude: ['backstage-exclude'], - include: ['backstage-include'], + it('apply full update on scheduled execution with topic exclusion taking priority over topic inclusion', async () => { + const config = new ConfigReader({ + catalog: { + providers: { + github: { + myProvider: { + organization: 'test-org', + catalogPath: 'custom/path/catalog-custom.yaml', + filters: { + branch: 'main', + repository: 'test-.*', + topic: { + exclude: ['backstage-exclude'], + include: ['backstage-include'], + }, }, }, }, }, }, - }, - }); - const schedule = new PersistingTaskRunner(); - const entityProviderConnection: EntityProviderConnection = { - applyMutation: jest.fn(), - refresh: jest.fn(), - }; + }); + const schedule = new PersistingTaskRunner(); + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; - const provider = GitHubEntityProvider.fromConfig(config, { - logger, - schedule, - })[0]; + const provider = GithubEntityProvider.fromConfig(config, { + logger, + schedule, + })[0]; - const mockGetOrganizationRepositories = jest.spyOn( - helpers, - 'getOrganizationRepositories', - ); + const mockGetOrganizationRepositories = jest.spyOn( + helpers, + 'getOrganizationRepositories', + ); mockGetOrganizationRepositories.mockReturnValue( Promise.resolve({ @@ -603,44 +606,114 @@ it('apply full update on scheduled execution with topic exclusion taking priorit __typename: 'Blob', id: 'abc123', text: 'some yaml', + }, + }, + ], + }), + ); + + await provider.connect(entityProviderConnection); + + const taskDef = schedule.getTasks()[0]; + expect(taskDef.id).toEqual('github-provider:myProvider:refresh'); + await (taskDef.fn as () => Promise)(); + + const url = `https://github.com/test-org/test-repo/blob/main/custom/path/catalog-custom.yaml`; + const expectedEntities = [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Location', + metadata: { + annotations: { + 'backstage.io/managed-by-location': `url:${url}`, + 'backstage.io/managed-by-origin-location': `url:${url}`, + }, + name: 'generated-5e4b9498097f15434e88c477cfba6c079aa8ca7f', + }, + spec: { + presence: 'optional', + target: `${url}`, + type: 'url', }, }, - ], - }), - ); + locationKey: 'github-provider:myProvider', + }, + ]; - await provider.connect(entityProviderConnection); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ + type: 'full', + entities: expectedEntities, + }); + }); - const taskDef = schedule.getTasks()[0]; - expect(taskDef.id).toEqual('github-provider:myProvider:refresh'); - await (taskDef.fn as () => Promise)(); - - const url = `https://github.com/test-org/test-repo/blob/main/custom/path/catalog-custom.yaml`; - const expectedEntities = [ - { - entity: { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Location', - metadata: { - annotations: { - 'backstage.io/managed-by-location': `url:${url}`, - 'backstage.io/managed-by-origin-location': `url:${url}`, + it('fail without schedule and scheduler', () => { + const config = new ConfigReader({ + catalog: { + providers: { + github: { + organization: 'test-org', }, - name: 'generated-5e4b9498097f15434e88c477cfba6c079aa8ca7f', - }, - spec: { - presence: 'optional', - target: `${url}`, - type: 'url', }, }, - locationKey: 'github-provider:myProvider', - }, - ]; + }); - expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1); - expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ - type: 'full', - entities: expectedEntities, + expect(() => + GithubEntityProvider.fromConfig(config, { + logger, + }), + ).toThrow('Either schedule or scheduler must be provided'); + }); + + it('fail with scheduler but no schedule config', () => { + const scheduler = { + createScheduledTaskRunner: (_: any) => jest.fn(), + } as unknown as PluginTaskScheduler; + const config = new ConfigReader({ + catalog: { + providers: { + github: { + organization: 'test-org', + }, + }, + }, + }); + + expect(() => + GithubEntityProvider.fromConfig(config, { + logger, + scheduler, + }), + ).toThrow( + 'No schedule provided neither via code nor config for github-provider:default', + ); + }); + + it('single simple provider config with schedule in config', async () => { + const schedule = new PersistingTaskRunner(); + const scheduler = { + createScheduledTaskRunner: (_: any) => schedule, + } as unknown as PluginTaskScheduler; + const config = new ConfigReader({ + catalog: { + providers: { + github: { + organization: 'test-org', + schedule: { + frequency: 'P1M', + timeout: 'PT3M', + }, + }, + }, + }, + }); + const providers = GithubEntityProvider.fromConfig(config, { + logger, + scheduler, + }); + + expect(providers).toHaveLength(1); + expect(providers[0].getProviderName()).toEqual('github-provider:default'); }); }); diff --git a/plugins/catalog-backend-module-github/src/providers/GitHubEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts similarity index 85% rename from plugins/catalog-backend-module-github/src/providers/GitHubEntityProvider.ts rename to plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts index 42dd6afa5d..2a93d97eb2 100644 --- a/plugins/catalog-backend-module-github/src/providers/GitHubEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { TaskRunner } from '@backstage/backend-tasks'; +import { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks'; import { Config } from '@backstage/config'; import { GithubCredentialsProvider, @@ -35,8 +35,8 @@ import * as uuid from 'uuid'; import { Logger } from 'winston'; import { readProviderConfigs, - GitHubEntityProviderConfig, -} from './GitHubEntityProviderConfig'; + GithubEntityProviderConfig, +} from './GithubEntityProviderConfig'; import { getOrganizationRepositories, Repository } from '../lib/github'; import { satisfiesTopicFilter } from '../lib/util'; @@ -48,8 +48,8 @@ import { satisfiesTopicFilter } from '../lib/util'; * * @public */ -export class GitHubEntityProvider implements EntityProvider { - private readonly config: GitHubEntityProviderConfig; +export class GithubEntityProvider implements EntityProvider { + private readonly config: GithubEntityProviderConfig; private readonly logger: Logger; private readonly integration: GitHubIntegrationConfig; private readonly scheduleFn: () => Promise; @@ -60,9 +60,14 @@ export class GitHubEntityProvider implements EntityProvider { config: Config, options: { logger: Logger; - schedule: TaskRunner; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; }, - ): GitHubEntityProvider[] { + ): GithubEntityProvider[] { + if (!options.schedule && !options.scheduler) { + throw new Error('Either schedule or scheduler must be provided.'); + } + const integrations = ScmIntegrations.fromConfig(config); return readProviderConfigs(config).map(providerConfig => { @@ -75,27 +80,37 @@ export class GitHubEntityProvider implements EntityProvider { ); } - return new GitHubEntityProvider( + if (!options.schedule && !providerConfig.schedule) { + throw new Error( + `No schedule provided neither via code nor config for github-provider:${providerConfig.id}.`, + ); + } + + const taskRunner = + options.schedule ?? + options.scheduler!.createScheduledTaskRunner(providerConfig.schedule!); + + return new GithubEntityProvider( providerConfig, integration, options.logger, - options.schedule, + taskRunner, ); }); } private constructor( - config: GitHubEntityProviderConfig, + config: GithubEntityProviderConfig, integration: GitHubIntegration, logger: Logger, - schedule: TaskRunner, + taskRunner: TaskRunner, ) { this.config = config; this.integration = integration.config; this.logger = logger.child({ target: this.getProviderName(), }); - this.scheduleFn = this.createScheduleFn(schedule); + this.scheduleFn = this.createScheduleFn(taskRunner); this.githubCredentialsProvider = SingleInstanceGithubCredentialsProvider.create(integration.config); } @@ -111,14 +126,14 @@ export class GitHubEntityProvider implements EntityProvider { return await this.scheduleFn(); } - private createScheduleFn(schedule: TaskRunner): () => Promise { + private createScheduleFn(taskRunner: TaskRunner): () => Promise { return async () => { const taskId = `${this.getProviderName()}:refresh`; - return schedule.run({ + return taskRunner.run({ id: taskId, fn: async () => { const logger = this.logger.child({ - class: GitHubEntityProvider.prototype.constructor.name, + class: GithubEntityProvider.prototype.constructor.name, taskId, taskInstanceId: uuid.v4(), }); @@ -141,7 +156,7 @@ export class GitHubEntityProvider implements EntityProvider { const matchingTargets = this.matchesFilters(targets); const entities = matchingTargets .map(repository => this.createLocationUrl(repository)) - .map(GitHubEntityProvider.toLocationSpec) + .map(GithubEntityProvider.toLocationSpec) .map(location => { return { locationKey: this.getProviderName(), diff --git a/plugins/catalog-backend-module-github/src/providers/GitHubEntityProviderConfig.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubEntityProviderConfig.test.ts similarity index 85% rename from plugins/catalog-backend-module-github/src/providers/GitHubEntityProviderConfig.test.ts rename to plugins/catalog-backend-module-github/src/providers/GithubEntityProviderConfig.test.ts index 16f9d96010..8ca9e76aca 100644 --- a/plugins/catalog-backend-module-github/src/providers/GitHubEntityProviderConfig.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubEntityProviderConfig.test.ts @@ -15,7 +15,8 @@ */ import { ConfigReader } from '@backstage/config'; -import { readProviderConfigs } from './GitHubEntityProviderConfig'; +import { Duration } from 'luxon'; +import { readProviderConfigs } from './GithubEntityProviderConfig'; describe('readProviderConfigs', () => { afterEach(() => jest.resetAllMocks()); @@ -81,13 +82,22 @@ describe('readProviderConfigs', () => { organization: 'test-org1', host: 'ghe.internal.com', }, + providerWithSchedule: { + organization: 'test-org1', + schedule: { + frequency: 'PT30M', + timeout: { + minutes: 3, + }, + }, + }, }, }, }, }); const providerConfigs = readProviderConfigs(config); - expect(providerConfigs).toHaveLength(6); + expect(providerConfigs).toHaveLength(7); expect(providerConfigs[0]).toEqual({ id: 'providerOrganizationOnly', organization: 'test-org1', @@ -101,6 +111,7 @@ describe('readProviderConfigs', () => { exclude: undefined, }, }, + schedule: undefined, validateLocationsExist: false, }); expect(providerConfigs[1]).toEqual({ @@ -116,6 +127,7 @@ describe('readProviderConfigs', () => { exclude: undefined, }, }, + schedule: undefined, validateLocationsExist: false, }); expect(providerConfigs[2]).toEqual({ @@ -131,6 +143,7 @@ describe('readProviderConfigs', () => { exclude: undefined, }, }, + schedule: undefined, validateLocationsExist: false, }); expect(providerConfigs[3]).toEqual({ @@ -146,6 +159,7 @@ describe('readProviderConfigs', () => { exclude: undefined, }, }, + schedule: undefined, validateLocationsExist: false, }); expect(providerConfigs[4]).toEqual({ @@ -161,6 +175,7 @@ describe('readProviderConfigs', () => { exclude: ['backstage-exclude'], }, }, + schedule: undefined, validateLocationsExist: false, }); expect(providerConfigs[5]).toEqual({ @@ -176,6 +191,27 @@ describe('readProviderConfigs', () => { exclude: undefined, }, }, + schedule: undefined, + }); + expect(providerConfigs[6]).toEqual({ + id: 'providerWithSchedule', + organization: 'test-org1', + catalogPath: '/catalog-info.yaml', + host: 'github.com', + filters: { + repository: undefined, + branch: undefined, + topic: { + include: undefined, + exclude: undefined, + }, + }, + schedule: { + frequency: Duration.fromISO('PT30M'), + timeout: { + minutes: 3, + }, + }, validateLocationsExist: false, }); }); diff --git a/plugins/catalog-backend-module-github/src/providers/GitHubEntityProviderConfig.ts b/plugins/catalog-backend-module-github/src/providers/GithubEntityProviderConfig.ts similarity index 89% rename from plugins/catalog-backend-module-github/src/providers/GitHubEntityProviderConfig.ts rename to plugins/catalog-backend-module-github/src/providers/GithubEntityProviderConfig.ts index fd5482d1b6..e888743258 100644 --- a/plugins/catalog-backend-module-github/src/providers/GitHubEntityProviderConfig.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubEntityProviderConfig.ts @@ -14,12 +14,16 @@ * limitations under the License. */ +import { + readTaskScheduleDefinitionFromConfig, + TaskScheduleDefinition, +} from '@backstage/backend-tasks'; import { Config } from '@backstage/config'; const DEFAULT_CATALOG_PATH = '/catalog-info.yaml'; const DEFAULT_PROVIDER_ID = 'default'; -export type GitHubEntityProviderConfig = { +export type GithubEntityProviderConfig = { id: string; catalogPath: string; organization: string; @@ -30,6 +34,7 @@ export type GitHubEntityProviderConfig = { topic?: GithubTopicFilters; }; validateLocationsExist: boolean; + schedule?: TaskScheduleDefinition; }; export type GithubTopicFilters = { @@ -39,7 +44,7 @@ export type GithubTopicFilters = { export function readProviderConfigs( config: Config, -): GitHubEntityProviderConfig[] { +): GithubEntityProviderConfig[] { const providersConfig = config.getOptionalConfig('catalog.providers.github'); if (!providersConfig) { return []; @@ -60,7 +65,7 @@ export function readProviderConfigs( function readProviderConfig( id: string, config: Config, -): GitHubEntityProviderConfig { +): GithubEntityProviderConfig { const organization = config.getString('organization'); const catalogPath = config.getOptionalString('catalogPath') ?? DEFAULT_CATALOG_PATH; @@ -84,6 +89,10 @@ function readProviderConfig( ); } + const schedule = config.has('schedule') + ? readTaskScheduleDefinitionFromConfig(config.getConfig('schedule')) + : undefined; + return { id, catalogPath, @@ -99,9 +108,11 @@ function readProviderConfig( exclude: topicFilterExclude, }, }, + schedule, validateLocationsExist, }; } + /** * Compiles a RegExp while enforcing the pattern to contain * the start-of-line and end-of-line anchors. diff --git a/plugins/catalog-backend-module-github/src/providers/GitHubOrgEntityProvider.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts similarity index 97% rename from plugins/catalog-backend-module-github/src/providers/GitHubOrgEntityProvider.test.ts rename to plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts index 2d05346f01..1d8d125d91 100644 --- a/plugins/catalog-backend-module-github/src/providers/GitHubOrgEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.test.ts @@ -23,13 +23,13 @@ import { import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; import { graphql } from '@octokit/graphql'; import { - GitHubOrgEntityProvider, + GithubOrgEntityProvider, withLocations, -} from './GitHubOrgEntityProvider'; +} from './GithubOrgEntityProvider'; jest.mock('@octokit/graphql'); -describe('GitHubOrgEntityProvider', () => { +describe('GithubOrgEntityProvider', () => { describe('read', () => { afterEach(() => jest.resetAllMocks()); @@ -100,7 +100,7 @@ describe('GitHubOrgEntityProvider', () => { getCredentials: mockGetCredentials, }; - const entityProvider = new GitHubOrgEntityProvider({ + const entityProvider = new GithubOrgEntityProvider({ id: 'my-id', githubCredentialsProvider, orgUrl: 'https://github.com/backstage', diff --git a/plugins/catalog-backend-module-github/src/providers/GitHubOrgEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts similarity index 92% rename from plugins/catalog-backend-module-github/src/providers/GitHubOrgEntityProvider.ts rename to plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts index bb994c193c..f8fafbe53c 100644 --- a/plugins/catalog-backend-module-github/src/providers/GitHubOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts @@ -41,15 +41,15 @@ import { buildOrgHierarchy, getOrganizationTeams, getOrganizationUsers, - parseGitHubOrgUrl, + parseGithubOrgUrl, } from '../lib'; /** - * Options for {@link GitHubOrgEntityProvider}. + * Options for {@link GithubOrgEntityProvider}. * * @public */ -export interface GitHubOrgEntityProviderOptions { +export interface GithubOrgEntityProviderOptions { /** * A unique, stable identifier for this provider. * @@ -96,12 +96,12 @@ export interface GitHubOrgEntityProviderOptions { * * @public */ -export class GitHubOrgEntityProvider implements EntityProvider { +export class GithubOrgEntityProvider implements EntityProvider { private readonly credentialsProvider: GithubCredentialsProvider; private connection?: EntityProviderConnection; private scheduleFn?: () => Promise; - static fromConfig(config: Config, options: GitHubOrgEntityProviderOptions) { + static fromConfig(config: Config, options: GithubOrgEntityProviderOptions) { const integrations = ScmIntegrations.fromConfig(config); const gitHubConfig = integrations.github.byUrl(options.orgUrl)?.config; @@ -115,7 +115,7 @@ export class GitHubOrgEntityProvider implements EntityProvider { target: options.orgUrl, }); - const provider = new GitHubOrgEntityProvider({ + const provider = new GithubOrgEntityProvider({ id: options.id, orgUrl: options.orgUrl, logger, @@ -146,7 +146,7 @@ export class GitHubOrgEntityProvider implements EntityProvider { /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ getProviderName() { - return `GitHubOrgEntityProvider:${this.options.id}`; + return `GithubOrgEntityProvider:${this.options.id}`; } /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ @@ -176,7 +176,7 @@ export class GitHubOrgEntityProvider implements EntityProvider { headers, }); - const { org } = parseGitHubOrgUrl(this.options.orgUrl); + const { org } = parseGithubOrgUrl(this.options.orgUrl); const { users } = await getOrganizationUsers(client, org, tokenType); const { groups, groupMemberUsers } = await getOrganizationTeams( client, @@ -202,7 +202,7 @@ export class GitHubOrgEntityProvider implements EntityProvider { markCommitComplete(); } - private schedule(schedule: GitHubOrgEntityProviderOptions['schedule']) { + private schedule(schedule: GithubOrgEntityProviderOptions['schedule']) { if (!schedule || schedule === 'manual') { return; } @@ -213,7 +213,7 @@ export class GitHubOrgEntityProvider implements EntityProvider { id, fn: async () => { const logger = this.options.logger.child({ - class: GitHubOrgEntityProvider.prototype.constructor.name, + class: GithubOrgEntityProvider.prototype.constructor.name, taskId: id, taskInstanceId: uuid.v4(), }); diff --git a/plugins/catalog-backend-module-github/src/service/GithubEntityProviderCatalogModule.test.ts b/plugins/catalog-backend-module-github/src/service/GithubEntityProviderCatalogModule.test.ts new file mode 100644 index 0000000000..b555927d2c --- /dev/null +++ b/plugins/catalog-backend-module-github/src/service/GithubEntityProviderCatalogModule.test.ts @@ -0,0 +1,84 @@ +/* + * Copyright 2022 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 { getVoidLogger } from '@backstage/backend-common'; +import { + configServiceRef, + loggerServiceRef, + schedulerServiceRef, +} from '@backstage/backend-plugin-api'; +import { + PluginTaskScheduler, + TaskScheduleDefinition, +} from '@backstage/backend-tasks'; +import { startTestBackend } from '@backstage/backend-test-utils'; +import { ConfigReader } from '@backstage/config'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node'; +import { Duration } from 'luxon'; +import { githubEntityProviderCatalogModule } from './GithubEntityProviderCatalogModule'; +import { GithubEntityProvider } from '../providers/GithubEntityProvider'; + +describe('githubEntityProviderCatalogModule', () => { + it('should register provider at the catalog extension point', async () => { + let addedProviders: Array | undefined; + let usedSchedule: TaskScheduleDefinition | undefined; + + const extensionPoint = { + addEntityProvider: (providers: any) => { + addedProviders = providers; + }, + }; + const runner = jest.fn(); + const scheduler = { + createScheduledTaskRunner: (schedule: TaskScheduleDefinition) => { + usedSchedule = schedule; + return runner; + }, + } as unknown as PluginTaskScheduler; + + const config = new ConfigReader({ + catalog: { + providers: { + github: { + organization: 'module-test', + schedule: { + frequency: 'P1M', + timeout: 'PT3M', + }, + }, + }, + }, + }); + + await startTestBackend({ + extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], + services: [ + [configServiceRef, config], + [loggerServiceRef, getVoidLogger()], + [schedulerServiceRef, scheduler], + ], + features: [githubEntityProviderCatalogModule()], + }); + + expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M')); + expect(usedSchedule?.timeout).toEqual(Duration.fromISO('PT3M')); + expect(addedProviders?.length).toEqual(1); + expect(addedProviders?.pop()?.getProviderName()).toEqual( + 'github-provider:default', + ); + expect(runner).not.toHaveBeenCalled(); + }); +}); diff --git a/plugins/catalog-backend-module-github/src/module.ts b/plugins/catalog-backend-module-github/src/service/GithubEntityProviderCatalogModule.ts similarity index 61% rename from plugins/catalog-backend-module-github/src/module.ts rename to plugins/catalog-backend-module-github/src/service/GithubEntityProviderCatalogModule.ts index 0391855618..8fdee886a3 100644 --- a/plugins/catalog-backend-module-github/src/module.ts +++ b/plugins/catalog-backend-module-github/src/service/GithubEntityProviderCatalogModule.ts @@ -21,46 +21,30 @@ import { loggerServiceRef, schedulerServiceRef, } from '@backstage/backend-plugin-api'; -import { TaskScheduleDefinition } from '@backstage/backend-tasks'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node'; -import { GitHubEntityProvider } from './providers/GitHubEntityProvider'; +import { GithubEntityProvider } from '../providers/GithubEntityProvider'; /** - * Options for {@link githubEntityProviderCatalogModule}. - * - * @alpha - */ -export type GithubEntityProviderCatalogModuleOptions = { - schedule?: TaskScheduleDefinition; -}; - -/** - * Registers the GitHubEntityProvider with the catalog processing extension point. + * Registers the {@link GithubEntityProvider} with the catalog processing extension point. * * @alpha */ export const githubEntityProviderCatalogModule = createBackendModule({ pluginId: 'catalog', moduleId: 'githubEntityProvider', - register(env, options?: GithubEntityProviderCatalogModuleOptions) { + register(env) { env.registerInit({ deps: { - config: configServiceRef, catalog: catalogProcessingExtensionPoint, + config: configServiceRef, logger: loggerServiceRef, scheduler: schedulerServiceRef, }, - async init({ config, catalog, logger, scheduler }) { - const scheduleDef = options?.schedule ?? { - frequency: { seconds: 600 }, - timeout: { seconds: 900 }, - initialDelay: { seconds: 3 }, - }; - + async init({ catalog, config, logger, scheduler }) { catalog.addEntityProvider( - GitHubEntityProvider.fromConfig(config, { + GithubEntityProvider.fromConfig(config, { logger: loggerToWinstonLogger(logger), - schedule: scheduler.createScheduledTaskRunner(scheduleDef), + scheduler, }), ); }, diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index add7b604d3..65979281e7 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.1.8 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## 0.1.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + ## 0.1.8-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/api-report.md b/plugins/catalog-backend-module-gitlab/api-report.md index 1009da8209..9ed90a2c42 100644 --- a/plugins/catalog-backend-module-gitlab/api-report.md +++ b/plugins/catalog-backend-module-gitlab/api-report.md @@ -3,6 +3,7 @@ > 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 { CatalogProcessor } from '@backstage/plugin-catalog-backend'; import { CatalogProcessorEmit } from '@backstage/plugin-catalog-backend'; import { Config } from '@backstage/config'; @@ -10,6 +11,7 @@ import { EntityProvider } from '@backstage/plugin-catalog-backend'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; import { LocationSpec } from '@backstage/plugin-catalog-backend'; import { Logger } from 'winston'; +import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { TaskRunner } from '@backstage/backend-tasks'; // @public @@ -21,7 +23,8 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { config: Config, options: { logger: Logger; - schedule: TaskRunner; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; }, ): GitlabDiscoveryEntityProvider[]; // (undocumented) @@ -30,6 +33,11 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { refresh(logger: Logger): Promise; } +// @alpha +export const gitlabDiscoveryEntityProviderCatalogModule: ( + options?: undefined, +) => BackendFeature; + // @public export class GitLabDiscoveryProcessor implements CatalogProcessor { // (undocumented) diff --git a/plugins/catalog-backend-module-gitlab/config.d.ts b/plugins/catalog-backend-module-gitlab/config.d.ts index 82f1a5db7c..ac75a04c2a 100644 --- a/plugins/catalog-backend-module-gitlab/config.d.ts +++ b/plugins/catalog-backend-module-gitlab/config.d.ts @@ -14,11 +14,10 @@ * limitations under the License. */ +import { TaskScheduleDefinitionConfig } from '@backstage/backend-tasks'; + export interface Config { catalog?: { - /** - * List of provider-specific options and attributes - */ providers?: { /** * GitlabDiscoveryEntityProvider configuration @@ -28,27 +27,27 @@ export interface Config { { /** * (Required) Gitlab's host name. - * @visibility backend */ host: string; /** * (Optional) Gitlab's group[/subgroup] where the discovery is done. * If not defined the whole project will be scanned. - * @visibility backend */ group?: string; /** * (Optional) Default branch to read the catalog-info.yaml file. * If not set, 'master' will be used. - * @visibility backend */ branch?: string; /** * (Optional) The name used for the catalog file. * If not set, 'catalog-info.yaml' will be used. - * @visibility backend */ entityFilename?: string; + /** + * (Optional) TaskScheduleDefinition for the refresh. + */ + schedule?: TaskScheduleDefinitionConfig; } >; }; diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index 510a4eb3be..a0a0cfc38f 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,12 +1,13 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", "description": "A Backstage catalog backend module that helps integrate towards GitLab", - "version": "0.1.8-next.1", + "version": "0.1.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { "access": "public", + "alphaTypes": "dist/index.alpha.d.ts", "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, @@ -23,22 +24,24 @@ "backstage" ], "scripts": { - "build": "backstage-cli package build", + "start": "backstage-cli package start", + "build": "backstage-cli package build --experimental-type-build", "lint": "backstage-cli package lint", "test": "backstage-cli package test", "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", - "clean": "backstage-cli package clean", - "start": "backstage-cli package start" + "clean": "backstage-cli package clean" }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/backend-tasks": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", "@backstage/types": "workspace:^", "lodash": "^4.17.21", "msw": "^0.47.0", @@ -50,9 +53,13 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@types/lodash": "^4.14.151", - "@types/uuid": "^8.0.0" + "@types/uuid": "^8.0.0", + "luxon": "^3.0.0" }, "files": [ + "alpha", + "config.d.ts", "dist" - ] + ], + "configSchema": "config.d.ts" } diff --git a/plugins/catalog-backend-module-gitlab/src/index.ts b/plugins/catalog-backend-module-gitlab/src/index.ts index 3182210c81..27aac9e78f 100644 --- a/plugins/catalog-backend-module-gitlab/src/index.ts +++ b/plugins/catalog-backend-module-gitlab/src/index.ts @@ -22,3 +22,4 @@ export { GitLabDiscoveryProcessor } from './GitLabDiscoveryProcessor'; export { GitlabDiscoveryEntityProvider } from './providers'; +export { gitlabDiscoveryEntityProviderCatalogModule } from './service/GitlabDiscoveryEntityProviderCatalogModule'; diff --git a/plugins/catalog-backend-module-gitlab/src/lib/types.ts b/plugins/catalog-backend-module-gitlab/src/lib/types.ts index 8a3c33c8f2..86fe1aa385 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/types.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/types.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { TaskScheduleDefinition } from '@backstage/backend-tasks'; + export type GitlabGroupDescription = { id: number; web_url: string; @@ -36,4 +38,5 @@ export type GitlabProviderConfig = { branch: string; catalogFile: string; projectPattern: RegExp; + schedule?: TaskScheduleDefinition; }; diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.test.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.test.ts index 3f3c4e7f91..ae19edc48c 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.test.ts @@ -15,13 +15,17 @@ */ import { getVoidLogger } from '@backstage/backend-common'; -import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks'; +import { + PluginTaskScheduler, + TaskInvocationDefinition, + TaskRunner, +} from '@backstage/backend-tasks'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; import { ConfigReader } from '@backstage/config'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; -import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; -import { GitlabDiscoveryEntityProvider } from './GitlabDiscoveryEntityProvider'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; +import { GitlabDiscoveryEntityProvider } from './GitlabDiscoveryEntityProvider'; class PersistingTaskRunner implements TaskRunner { private tasks: TaskInvocationDefinition[] = []; @@ -338,4 +342,111 @@ describe('GitlabDiscoveryEntityProvider', () => { ], }); }); + + it('fail without schedule and scheduler', () => { + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'test-gitlab', + apiBaseUrl: 'https://api.gitlab.example/api/v4', + token: '1234', + }, + ], + }, + catalog: { + providers: { + gitlab: { + 'test-id': { + host: 'test-gitlab', + group: 'test-group', + }, + }, + }, + }, + }); + + expect(() => + GitlabDiscoveryEntityProvider.fromConfig(config, { + logger, + }), + ).toThrow('Either schedule or scheduler must be provided'); + }); + + it('fail with scheduler but no schedule config', () => { + const scheduler = { + createScheduledTaskRunner: (_: any) => jest.fn(), + } as unknown as PluginTaskScheduler; + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'test-gitlab', + apiBaseUrl: 'https://api.gitlab.example/api/v4', + token: '1234', + }, + ], + }, + catalog: { + providers: { + gitlab: { + 'test-id': { + host: 'test-gitlab', + group: 'test-group', + }, + }, + }, + }, + }); + + expect(() => + GitlabDiscoveryEntityProvider.fromConfig(config, { + logger, + scheduler, + }), + ).toThrow( + 'No schedule provided neither via code nor config for GitlabDiscoveryEntityProvider:test-id', + ); + }); + + it('single simple provider config with schedule in config', async () => { + const schedule = new PersistingTaskRunner(); + const scheduler = { + createScheduledTaskRunner: (_: any) => schedule, + } as unknown as PluginTaskScheduler; + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'test-gitlab', + apiBaseUrl: 'https://api.gitlab.example/api/v4', + token: '1234', + }, + ], + }, + catalog: { + providers: { + gitlab: { + 'test-id': { + host: 'test-gitlab', + group: 'test-group', + schedule: { + frequency: 'PT30M', + timeout: 'PT3M', + }, + }, + }, + }, + }, + }); + const providers = GitlabDiscoveryEntityProvider.fromConfig(config, { + logger, + scheduler, + }); + + expect(providers).toHaveLength(1); + expect(providers[0].getProviderName()).toEqual( + 'GitlabDiscoveryEntityProvider:test-id', + ); + }); }); diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts index ffb123edf7..cb4629b820 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts @@ -14,14 +14,16 @@ * limitations under the License. */ -import { TaskRunner } from '@backstage/backend-tasks'; +import { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks'; import { Config } from '@backstage/config'; import { GitLabIntegration, ScmIntegrations } from '@backstage/integration'; import { EntityProvider, EntityProviderConnection, + LocationSpec, + locationSpecToLocationEntity, } from '@backstage/plugin-catalog-backend'; -import { LocationSpec } from '@backstage/plugin-catalog-backend'; +import * as uuid from 'uuid'; import { Logger } from 'winston'; import { GitLabClient, @@ -30,8 +32,6 @@ import { paginated, readGitlabConfigs, } from '../lib'; -import * as uuid from 'uuid'; -import { locationSpecToLocationEntity } from '@backstage/plugin-catalog-backend'; type Result = { scanned: number; @@ -51,8 +51,16 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { static fromConfig( config: Config, - options: { logger: Logger; schedule: TaskRunner }, + options: { + logger: Logger; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; + }, ): GitlabDiscoveryEntityProvider[] { + if (!options.schedule && !options.scheduler) { + throw new Error('Either schedule or scheduler must be provided.'); + } + const providerConfigs = readGitlabConfigs(config); const integrations = ScmIntegrations.fromConfig(config).gitlab; const providers: GitlabDiscoveryEntityProvider[] = []; @@ -64,11 +72,23 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { `No gitlab integration found that matches host ${providerConfig.host}`, ); } + + if (!options.schedule && !providerConfig.schedule) { + throw new Error( + `No schedule provided neither via code nor config for GitlabDiscoveryEntityProvider:${providerConfig.id}.`, + ); + } + + const taskRunner = + options.schedule ?? + options.scheduler!.createScheduledTaskRunner(providerConfig.schedule!); + providers.push( new GitlabDiscoveryEntityProvider({ ...options, config: providerConfig, integration, + taskRunner, }), ); }); @@ -79,14 +99,14 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { config: GitlabProviderConfig; integration: GitLabIntegration; logger: Logger; - schedule: TaskRunner; + taskRunner: TaskRunner; }) { this.config = options.config; this.integration = options.integration; this.logger = options.logger.child({ target: this.getProviderName(), }); - this.scheduleFn = this.createScheduleFn(options.schedule); + this.scheduleFn = this.createScheduleFn(options.taskRunner); } getProviderName(): string { @@ -98,10 +118,10 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { await this.scheduleFn(); } - private createScheduleFn(schedule: TaskRunner): () => Promise { + private createScheduleFn(taskRunner: TaskRunner): () => Promise { return async () => { const taskId = `${this.getProviderName()}:refresh`; - return schedule.run({ + return taskRunner.run({ id: taskId, fn: async () => { const logger = this.logger.child({ diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts index cb36e5166c..6b337bf7cd 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts @@ -15,6 +15,7 @@ */ import { ConfigReader } from '@backstage/config'; +import { Duration } from 'luxon'; import { readGitlabConfigs } from './config'; describe('config', () => { @@ -53,6 +54,7 @@ describe('config', () => { host: 'host', catalogFile: 'catalog-info.yaml', projectPattern: /[\s\S]*/, + schedule: undefined, }), ); }); @@ -83,6 +85,49 @@ describe('config', () => { host: 'host', catalogFile: 'custom-file.yaml', projectPattern: /[\s\S]*/, + schedule: undefined, + }), + ); + }); + + it('valid config with schedule', () => { + const config = new ConfigReader({ + catalog: { + providers: { + gitlab: { + test: { + group: 'group', + host: 'host', + schedule: { + frequency: 'PT30M', + timeout: { + minutes: 3, + }, + }, + }, + }, + }, + }, + }); + + const result = readGitlabConfigs(config); + expect(result).toHaveLength(1); + result.forEach(r => + expect(r).toStrictEqual({ + id: 'test', + group: 'group', + branch: 'master', + host: 'host', + catalogFile: 'catalog-info.yaml', + projectPattern: /[\s\S]*/, + schedule: { + frequency: Duration.fromISO('PT30M'), + timeout: { + minutes: 3, + }, + initialDelay: undefined, + scope: undefined, + }, }), ); }); diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.ts index 392922860f..3dfc00745e 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/config.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { readTaskScheduleDefinitionFromConfig } from '@backstage/backend-tasks'; import { Config } from '@backstage/config'; import { GitlabProviderConfig } from '../lib'; @@ -35,6 +36,10 @@ function readGitlabConfig(id: string, config: Config): GitlabProviderConfig { config.getOptionalString('projectPattern') ?? /[\s\S]*/, ); + const schedule = config.has('schedule') + ? readTaskScheduleDefinitionFromConfig(config.getConfig('schedule')) + : undefined; + return { id, group, @@ -42,6 +47,7 @@ function readGitlabConfig(id: string, config: Config): GitlabProviderConfig { host, catalogFile, projectPattern, + schedule, }; } diff --git a/plugins/catalog-backend-module-gitlab/src/service/GitlabDiscoveryEntityProviderCatalogModule.test.ts b/plugins/catalog-backend-module-gitlab/src/service/GitlabDiscoveryEntityProviderCatalogModule.test.ts new file mode 100644 index 0000000000..0b803734b1 --- /dev/null +++ b/plugins/catalog-backend-module-gitlab/src/service/GitlabDiscoveryEntityProviderCatalogModule.test.ts @@ -0,0 +1,96 @@ +/* + * Copyright 2022 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 { getVoidLogger } from '@backstage/backend-common'; +import { + configServiceRef, + loggerServiceRef, + schedulerServiceRef, +} from '@backstage/backend-plugin-api'; +import { + PluginTaskScheduler, + TaskScheduleDefinition, +} from '@backstage/backend-tasks'; +import { startTestBackend } from '@backstage/backend-test-utils'; +import { ConfigReader } from '@backstage/config'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node'; +import { Duration } from 'luxon'; +import { gitlabDiscoveryEntityProviderCatalogModule } from './GitlabDiscoveryEntityProviderCatalogModule'; +import { GitlabDiscoveryEntityProvider } from '../providers'; + +describe('gitlabDiscoveryEntityProviderCatalogModule', () => { + it('should register provider at the catalog extension point', async () => { + let addedProviders: Array | undefined; + let usedSchedule: TaskScheduleDefinition | undefined; + + const extensionPoint = { + addEntityProvider: (providers: any) => { + addedProviders = providers; + }, + }; + const runner = jest.fn(); + const scheduler = { + createScheduledTaskRunner: (schedule: TaskScheduleDefinition) => { + usedSchedule = schedule; + return runner; + }, + } as unknown as PluginTaskScheduler; + + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'test-gitlab', + apiBaseUrl: 'https://api.gitlab.example/api/v4', + token: '1234', + }, + ], + }, + catalog: { + providers: { + gitlab: { + 'test-id': { + host: 'test-gitlab', + group: 'test-group', + schedule: { + frequency: 'P1M', + timeout: 'PT3M', + }, + }, + }, + }, + }, + }); + + await startTestBackend({ + extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], + services: [ + [configServiceRef, config], + [loggerServiceRef, getVoidLogger()], + [schedulerServiceRef, scheduler], + ], + features: [gitlabDiscoveryEntityProviderCatalogModule()], + }); + + expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M')); + expect(usedSchedule?.timeout).toEqual(Duration.fromISO('PT3M')); + expect(addedProviders?.length).toEqual(1); + expect(addedProviders?.pop()?.getProviderName()).toEqual( + 'GitlabDiscoveryEntityProvider:test-id', + ); + expect(runner).not.toHaveBeenCalled(); + }); +}); diff --git a/plugins/catalog-backend-module-gitlab/src/service/GitlabDiscoveryEntityProviderCatalogModule.ts b/plugins/catalog-backend-module-gitlab/src/service/GitlabDiscoveryEntityProviderCatalogModule.ts new file mode 100644 index 0000000000..dc11841e20 --- /dev/null +++ b/plugins/catalog-backend-module-gitlab/src/service/GitlabDiscoveryEntityProviderCatalogModule.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2022 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 { + createBackendModule, + loggerToWinstonLogger, + configServiceRef, + loggerServiceRef, + schedulerServiceRef, +} from '@backstage/backend-plugin-api'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node'; +import { GitlabDiscoveryEntityProvider } from '../providers'; + +/** + * Registers the GitlabDiscoveryEntityProvider with the catalog processing extension point. + * + * @alpha + */ +export const gitlabDiscoveryEntityProviderCatalogModule = createBackendModule({ + pluginId: 'catalog', + moduleId: 'gitlabDiscoveryEntityProvider', + register(env) { + env.registerInit({ + deps: { + config: configServiceRef, + catalog: catalogProcessingExtensionPoint, + logger: loggerServiceRef, + scheduler: schedulerServiceRef, + }, + async init({ config, catalog, logger, scheduler }) { + catalog.addEntityProvider( + GitlabDiscoveryEntityProvider.fromConfig(config, { + logger: loggerToWinstonLogger(logger), + scheduler, + }), + ); + }, + }); + }, +}); diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 305eed8686..8045a29598 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.5.4 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## 0.5.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + ## 0.5.4-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index eaa3929ade..eb97de27d0 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", "description": "A Backstage catalog backend module that helps integrate towards LDAP", - "version": "0.5.4-next.1", + "version": "0.5.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index 2da4607869..4112931043 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.4.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/backend-tasks@0.3.6 + - @backstage/config@1.0.3 + +## 0.4.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-tasks@0.3.6-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + ## 0.4.3-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index d3ac35f743..df63bc71dd 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", - "version": "0.4.3-next.1", + "version": "0.4.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index d098727b3e..a11be4a5f2 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.1.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0 + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-backend@1.5.0 + - @backstage/config@1.0.3 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## 0.1.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0-next.2 + - @backstage/plugin-catalog-backend@1.5.0-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + ## 0.1.3-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index dded553b09..1f32a1a4f2 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-openapi", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", - "version": "0.1.3-next.1", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-openapi/src/lib/bundle.test.ts b/plugins/catalog-backend-module-openapi/src/lib/bundle.test.ts index 0ecd9f7fd1..57ecd54f26 100644 --- a/plugins/catalog-backend-module-openapi/src/lib/bundle.test.ts +++ b/plugins/catalog-backend-module-openapi/src/lib/bundle.test.ts @@ -88,6 +88,33 @@ describe('bundleOpenApiSpecification', () => { resolveUrl, ); + expect(result).toEqual(expectedResult.trimStart()); + }); + it('should use the urlreaders to fetch $refs', async () => { + const spec = ` + openapi: "3.0.0" + info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT + servers: + - url: http://petstore.swagger.io/v1 + paths: + /pets: + get: + $ref: "https://foo.com/paths/pets/list.yaml" + `; + + read.mockResolvedValue(list); + + const result = await bundleOpenApiSpecification( + spec, + 'https://github.com/owner/repo/blob/main/catalog-info.yaml', + read, + resolveUrl, + ); + expect(result).toEqual(expectedResult.trimStart()); }); }); diff --git a/plugins/catalog-backend-module-openapi/src/lib/bundle.ts b/plugins/catalog-backend-module-openapi/src/lib/bundle.ts index 6ac8d211e2..8dd880c234 100644 --- a/plugins/catalog-backend-module-openapi/src/lib/bundle.ts +++ b/plugins/catalog-backend-module-openapi/src/lib/bundle.ts @@ -47,11 +47,21 @@ export async function bundleOpenApiSpecification( return await read(url); }, }; + const httpUrlReaderResolver: SwaggerParser.ResolverOptions = { + canRead: ref => { + const protocol = getProtocol(ref.url); + return protocol === 'http' || protocol === 'https'; + }, + read: async ref => { + const url = resolveUrl(ref.url, baseUrl); + return await read(url); + }, + }; const options: SwaggerParser.Options = { resolve: { file: fileUrlReaderResolver, - http: true, + http: httpUrlReaderResolver, }, }; const specObject = parse(specification); diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index d3b24efb6f..60ecdae3b9 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,78 @@ # @backstage/plugin-catalog-backend +## 1.5.0 + +### Minor Changes + +- b2e6cb6acf: Added a new method `addLocationAnalyzers` to the `CatalogBuilder`. With this you can add location analyzers to your catalog. These analyzers will be used by the /analyze-location endpoint to decide if the provided URL contains any catalog-info.yaml files already or not. + + Moved the following types from this package to `@backstage/plugin-catalog-backend`. + + - AnalyzeLocationResponse + - AnalyzeLocationRequest + - AnalyzeLocationExistingEntity + - AnalyzeLocationGenerateEntity + - AnalyzeLocationEntityField + +- eb25f7e12d: The exported permission rules and the API of `createCatalogConditionalDecision` have changed to reflect the breaking changes made to the `PermissionRule` type. Note that all involved types are exported from `@backstage/plugin-catalog-backend/alpha` + +### Patch Changes + +- 8cb6e10105: Fixed a bug where entities provided without a location key would always replace existing entities, rather than updating them. +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- 63296ebcd4: Allow Placeholder value to be any value, not only string. +- 74022e0163: Make sure to stitch entities correctly after deletion, to ensure that their relations are updated. +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0 + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-catalog-common@1.0.7 + - @backstage/plugin-permission-node@0.7.0 + - @backstage/backend-plugin-api@0.1.3 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/catalog-client@1.1.1 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-scaffolder-common@1.2.1 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/types@1.0.0 + +## 1.5.0-next.2 + +### Minor Changes + +- b2e6cb6acf: Added a new method `addLocationAnalyzers` to the `CatalogBuilder`. With this you can add location analyzers to your catalog. These analyzers will be used by the /analyze-location endpoint to decide if the provided URL contains any catalog-info.yaml files already or not. + + Moved the following types from this package to `@backstage/plugin-catalog-backend`. + + - AnalyzeLocationResponse + - AnalyzeLocationRequest + - AnalyzeLocationExistingEntity + - AnalyzeLocationGenerateEntity + - AnalyzeLocationEntityField + +- eb25f7e12d: The exported permission rules and the API of `createCatalogConditionalDecision` have changed to reflect the breaking changes made to the `PermissionRule` type. Note that all involved types are exported from `@backstage/plugin-catalog-backend/alpha` + +### Patch Changes + +- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md` +- Updated dependencies + - @backstage/plugin-catalog-node@1.2.0-next.2 + - @backstage/plugin-catalog-common@1.0.7-next.2 + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-permission-node@0.7.0-next.2 + - @backstage/backend-plugin-api@0.1.3-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/types@1.0.0 + - @backstage/plugin-scaffolder-common@1.2.1-next.2 + ## 1.4.1-next.1 ### Patch Changes diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index f18d396114..10b9afc291 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -5,6 +5,11 @@ ```ts /// +import { AnalyzeLocationEntityField as AnalyzeLocationEntityField_2 } from '@backstage/plugin-catalog-common'; +import { AnalyzeLocationExistingEntity as AnalyzeLocationExistingEntity_2 } from '@backstage/plugin-catalog-common'; +import { AnalyzeLocationGenerateEntity as AnalyzeLocationGenerateEntity_2 } from '@backstage/plugin-catalog-common'; +import { AnalyzeLocationRequest as AnalyzeLocationRequest_2 } from '@backstage/plugin-catalog-common'; +import { AnalyzeLocationResponse as AnalyzeLocationResponse_2 } from '@backstage/plugin-catalog-common'; import { BackendFeature } from '@backstage/backend-plugin-api'; import { CatalogApi } from '@backstage/catalog-client'; import { CatalogEntityDocument } from '@backstage/plugin-catalog-common'; @@ -40,6 +45,7 @@ import { PermissionCondition } from '@backstage/plugin-permission-common'; import { PermissionCriteria } from '@backstage/plugin-permission-common'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; import { PermissionRule } from '@backstage/plugin-permission-node'; +import { PermissionRuleParams } from '@backstage/plugin-permission-common'; import { PluginDatabaseManager } from '@backstage/backend-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { processingResult } from '@backstage/plugin-catalog-node'; @@ -51,39 +57,25 @@ import { TokenManager } from '@backstage/backend-common'; import { UrlReader } from '@backstage/backend-common'; import { Validators } from '@backstage/catalog-model'; -// @public (undocumented) -export type AnalyzeLocationEntityField = { - field: string; - state: - | 'analysisSuggestedValue' - | 'analysisSuggestedNoValue' - | 'needsUserInput'; - value: string | null; - description: string; -}; +// @public @deprecated +export type AnalyzeLocationEntityField = AnalyzeLocationEntityField_2; -// @public -export type AnalyzeLocationExistingEntity = { - location: LocationSpec; - isRegistered: boolean; - entity: Entity; -}; +// @public @deprecated +export type AnalyzeLocationExistingEntity = AnalyzeLocationExistingEntity_2; -// @public -export type AnalyzeLocationGenerateEntity = { - entity: RecursivePartial; - fields: AnalyzeLocationEntityField[]; -}; +// @public @deprecated +export type AnalyzeLocationGenerateEntity = AnalyzeLocationGenerateEntity_2; + +// @public @deprecated (undocumented) +export type AnalyzeLocationRequest = AnalyzeLocationRequest_2; + +// @public @deprecated (undocumented) +export type AnalyzeLocationResponse = AnalyzeLocationResponse_2; // @public (undocumented) -export type AnalyzeLocationRequest = { - location: LocationSpec; -}; - -// @public (undocumented) -export type AnalyzeLocationResponse = { - existingEntityFiles: AnalyzeLocationExistingEntity[]; - generateEntities: AnalyzeLocationGenerateEntity[]; +export type AnalyzeOptions = { + url: string; + catalogFilename?: string; }; // @public (undocumented) @@ -133,6 +125,9 @@ export class CatalogBuilder { addEntityProvider( ...providers: Array> ): CatalogBuilder; + addLocationAnalyzers( + ...analyzers: Array> + ): CatalogBuilder; // @alpha addPermissionRules( ...permissionRules: Array< @@ -177,37 +172,52 @@ export const catalogConditions: Conditions<{ Entity, EntitiesSearchFilter, 'catalog-entity', - [annotation: string, value?: string | undefined] + { + value?: string | undefined; + annotation: string; + } >; hasLabel: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [label: string] + { + label: string; + } >; hasMetadata: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [key: string, value?: string | undefined] + { + value?: string | undefined; + key: string; + } >; hasSpec: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [key: string, value?: string | undefined] + { + value?: string | undefined; + key: string; + } >; isEntityKind: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [kinds: string[]] + { + kinds: string[]; + } >; isEntityOwner: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [claims: string[]] + { + claims: string[]; + } >; }>; @@ -221,8 +231,9 @@ export type CatalogEnvironment = { }; // @alpha -export type CatalogPermissionRule = - PermissionRule; +export type CatalogPermissionRule< + TParams extends PermissionRuleParams = PermissionRuleParams, +> = PermissionRule; // @alpha export const catalogPlugin: (options?: undefined) => BackendFeature; @@ -280,12 +291,14 @@ export class CodeOwnersProcessor implements CatalogProcessor { export const createCatalogConditionalDecision: ( permission: ResourcePermission<'catalog-entity'>, conditions: PermissionCriteria< - PermissionCondition<'catalog-entity', unknown[]> + PermissionCondition<'catalog-entity', PermissionRuleParams> >, ) => ConditionalPolicyDecision; // @alpha -export const createCatalogPermissionRule: ( +export const createCatalogPermissionRule: < + TParams extends PermissionRuleParams = undefined, +>( rule: PermissionRule, ) => PermissionRule; @@ -448,37 +461,52 @@ export const permissionRules: { Entity, EntitiesSearchFilter, 'catalog-entity', - [annotation: string, value?: string | undefined] + { + value?: string | undefined; + annotation: string; + } >; hasLabel: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [label: string] + { + label: string; + } >; hasMetadata: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [key: string, value?: string | undefined] + { + value?: string | undefined; + key: string; + } >; hasSpec: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [key: string, value?: string | undefined] + { + value?: string | undefined; + key: string; + } >; isEntityKind: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [kinds: string[]] + { + kinds: string[]; + } >; isEntityOwner: PermissionRule< Entity, EntitiesSearchFilter, 'catalog-entity', - [claims: string[]] + { + claims: string[]; + } >; }; @@ -531,6 +559,14 @@ export type ProcessingIntervalFunction = () => number; export { processingResult }; +// @public (undocumented) +export type ScmLocationAnalyzer = { + supports(url: string): boolean; + analyze(options: AnalyzeOptions): Promise<{ + existing: AnalyzeLocationExistingEntity[]; + }>; +}; + // @public (undocumented) export class UrlReaderProcessor implements CatalogProcessor { constructor(options: { reader: UrlReader; logger: Logger }); diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 4d127a4d43..d3e44d47b1 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend", "description": "The Backstage backend plugin that provides the Backstage catalog", - "version": "1.4.1-next.1", + "version": "1.5.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts index b84dff7de8..761a0f8c0a 100644 --- a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts +++ b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts @@ -18,34 +18,32 @@ import { Logger } from 'winston'; import parseGitUrl from 'git-url-parse'; import { Entity } from '@backstage/catalog-model'; import { ScmIntegrationRegistry } from '@backstage/integration'; +import { LocationAnalyzer, ScmLocationAnalyzer } from './types'; import { AnalyzeLocationRequest, AnalyzeLocationResponse, - LocationAnalyzer, -} from './types'; +} from '@backstage/plugin-catalog-common'; export class RepoLocationAnalyzer implements LocationAnalyzer { private readonly logger: Logger; private readonly scmIntegrations: ScmIntegrationRegistry; + private readonly analyzers: ScmLocationAnalyzer[]; - constructor(logger: Logger, scmIntegrations: ScmIntegrationRegistry) { + constructor( + logger: Logger, + scmIntegrations: ScmIntegrationRegistry, + analyzers: ScmLocationAnalyzer[], + ) { this.logger = logger; this.scmIntegrations = scmIntegrations; + this.analyzers = analyzers; } async analyzeLocation( request: AnalyzeLocationRequest, ): Promise { - const { owner, name } = parseGitUrl(request.location.target); - const entity: Entity = { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: name, - }, - spec: { type: 'other', lifecycle: 'unknown' }, - }; - const integration = this.scmIntegrations.byUrl(request.location.target); + const { owner, name } = parseGitUrl(request.location.target); + let annotationPrefix; switch (integration?.type) { case 'azure': @@ -64,6 +62,33 @@ export class RepoLocationAnalyzer implements LocationAnalyzer { break; } + const analyzer = this.analyzers.find(a => + a.supports(request.location.target), + ); + if (analyzer) { + const analyzerResult = await analyzer.analyze({ + url: request.location.target, + }); + if (analyzerResult.existing.length > 0) { + this.logger.debug( + `entity for ${request.location.target} already exists.`, + ); + return { + existingEntityFiles: analyzerResult.existing, + generateEntities: [], + }; + } + } + + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: name, + }, + spec: { type: 'other', lifecycle: 'unknown' }, + }; + if (annotationPrefix) { entity.metadata.annotations = { [`${annotationPrefix}/project-slug`]: `${owner}/${name}`, diff --git a/plugins/catalog-backend/src/ingestion/index.ts b/plugins/catalog-backend/src/ingestion/index.ts index c97d029b5c..0b00809043 100644 --- a/plugins/catalog-backend/src/ingestion/index.ts +++ b/plugins/catalog-backend/src/ingestion/index.ts @@ -21,4 +21,6 @@ export type { AnalyzeLocationRequest, AnalyzeLocationResponse, LocationAnalyzer, + ScmLocationAnalyzer, + AnalyzeOptions, } from './types'; diff --git a/plugins/catalog-backend/src/ingestion/types.ts b/plugins/catalog-backend/src/ingestion/types.ts index 2602bd080f..78339b3b23 100644 --- a/plugins/catalog-backend/src/ingestion/types.ts +++ b/plugins/catalog-backend/src/ingestion/types.ts @@ -14,9 +14,57 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; -import { RecursivePartial } from '../util/RecursivePartial'; -import { LocationSpec } from '@backstage/plugin-catalog-node'; +import { + AnalyzeLocationRequest as NonDeprecatedAnalyzeLocationRequest, + AnalyzeLocationResponse as NonDeprecatedAnalyzeLocationResponse, + AnalyzeLocationExistingEntity as NonDeprecatedAnalyzeLocationExistingEntity, + AnalyzeLocationGenerateEntity as NonDeprecatedAnalyzeLocationGenerateEntity, + AnalyzeLocationEntityField as NonDeprecatedAnalyzeLocationEntityField, +} from '@backstage/plugin-catalog-common'; + +/** + * @public + * @deprecated use the same type from `@backstage/plugin-catalog-common` instead + */ +export type AnalyzeLocationRequest = NonDeprecatedAnalyzeLocationRequest; +/** + * @public + * @deprecated use the same type from `@backstage/plugin-catalog-common` instead + */ +export type AnalyzeLocationResponse = NonDeprecatedAnalyzeLocationResponse; + +/** + * If the folder pointed to already contained catalog info yaml files, they are + * read and emitted like this so that the frontend can inform the user that it + * located them and can make sure to register them as well if they weren't + * already + * @public + * @deprecated use the same type from `@backstage/plugin-catalog-common` instead + */ +export type AnalyzeLocationExistingEntity = + NonDeprecatedAnalyzeLocationExistingEntity; +/** + * This is some form of representation of what the analyzer could deduce. + * We should probably have a chat about how this can best be conveyed to + * the frontend. It'll probably contain a (possibly incomplete) entity, plus + * enough info for the frontend to know what form data to show to the user + * for overriding/completing the info. + * @public + * @deprecated use the same type from `@backstage/plugin-catalog-common` instead + */ +export type AnalyzeLocationGenerateEntity = + NonDeprecatedAnalyzeLocationGenerateEntity; + +/** + * + * This is where I get really vague. Something like this perhaps? Or it could be + * something like a json-schema that contains enough info for the frontend to + * be able to present a form and explanations + * @public + * @deprecated use the same type from `@backstage/plugin-catalog-common` instead + */ +export type AnalyzeLocationEntityField = + NonDeprecatedAnalyzeLocationEntityField; /** @public */ export type LocationAnalyzer = { @@ -30,71 +78,19 @@ export type LocationAnalyzer = { location: AnalyzeLocationRequest, ): Promise; }; - /** @public */ -export type AnalyzeLocationRequest = { - location: LocationSpec; +export type AnalyzeOptions = { + url: string; + catalogFilename?: string; }; /** @public */ -export type AnalyzeLocationResponse = { - existingEntityFiles: AnalyzeLocationExistingEntity[]; - generateEntities: AnalyzeLocationGenerateEntity[]; -}; - -/** - * If the folder pointed to already contained catalog info yaml files, they are - * read and emitted like this so that the frontend can inform the user that it - * located them and can make sure to register them as well if they weren't - * already - * @public - */ -export type AnalyzeLocationExistingEntity = { - location: LocationSpec; - isRegistered: boolean; - entity: Entity; -}; - -/** - * This is some form of representation of what the analyzer could deduce. - * We should probably have a chat about how this can best be conveyed to - * the frontend. It'll probably contain a (possibly incomplete) entity, plus - * enough info for the frontend to know what form data to show to the user - * for overriding/completing the info. - * @public - */ -export type AnalyzeLocationGenerateEntity = { - // Some form of partial representation of the entity - entity: RecursivePartial; - // Lists the suggestions that the user may want to override - fields: AnalyzeLocationEntityField[]; -}; - -// This is where I get really vague. Something like this perhaps? Or it could be -// something like a json-schema that contains enough info for the frontend to -// be able to present a form and explanations -/** @public */ -export type AnalyzeLocationEntityField = { - /** - * e.g. "spec.owner"? The frontend needs to know how to "inject" the field into the - * entity again if the user wants to change it - */ - field: string; - - /** The outcome of the analysis for this particular field */ - state: - | 'analysisSuggestedValue' - | 'analysisSuggestedNoValue' - | 'needsUserInput'; - - // If the analysis did suggest a value, this is where it would be. Not sure if we want - // to limit this to strings or if we want it to be any JsonValue - value: string | null; - /** - * A text to show to the user to inform about the choices made. Like, it could say - * "Found a CODEOWNERS file that covers this target, so we suggest leaving this - * field empty; which would currently make it owned by X" where X is taken from the - * codeowners file. - */ - description: string; +export type ScmLocationAnalyzer = { + /** The method that decides if this analyzer can work with the provided url */ + supports(url: string): boolean; + /** This function can return an array of already existing entities */ + analyze(options: AnalyzeOptions): Promise<{ + /** Existing entities in the analyzed location */ + existing: AnalyzeLocationExistingEntity[]; + }>; }; diff --git a/plugins/catalog-backend/src/permissions/rules/createPropertyRule.test.ts b/plugins/catalog-backend/src/permissions/rules/createPropertyRule.test.ts index cb11add9ed..5b34c98ca6 100644 --- a/plugins/catalog-backend/src/permissions/rules/createPropertyRule.test.ts +++ b/plugins/catalog-backend/src/permissions/rules/createPropertyRule.test.ts @@ -41,7 +41,9 @@ describe('createPropertyRule', () => { name: 'test-component', }, }, - 'org.name', + { + key: 'org.name', + }, ), ).toBe(false); }); @@ -57,7 +59,9 @@ describe('createPropertyRule', () => { tags: [], }, }, - 'tags', + { + key: 'tags', + }, ), ).toBe(false); }); @@ -75,7 +79,9 @@ describe('createPropertyRule', () => { }, }, }, - 'org.name', + { + key: 'org.name', + }, ), ).toBe(true); }); @@ -91,7 +97,9 @@ describe('createPropertyRule', () => { tags: ['java'], }, }, - 'tags', + { + key: 'tags', + }, ), ).toBe(true); }); @@ -108,8 +116,10 @@ describe('createPropertyRule', () => { name: 'test-component', }, }, - 'org.name', - 'test-org', + { + key: 'org.name', + value: 'test-org', + }, ), ).toBe(false); }); @@ -127,8 +137,10 @@ describe('createPropertyRule', () => { }, }, }, - 'org.name', - 'test-org', + { + key: 'org.name', + value: 'test-org', + }, ), ).toBe(false); }); @@ -144,8 +156,10 @@ describe('createPropertyRule', () => { tags: ['java'], }, }, - 'tags', - 'python', + { + key: 'tags', + value: 'python', + }, ), ).toBe(false); }); @@ -163,8 +177,10 @@ describe('createPropertyRule', () => { }, }, }, - 'org.name', - 'test-org', + { + key: 'org.name', + value: 'test-org', + }, ), ).toBe(true); }); @@ -180,8 +196,10 @@ describe('createPropertyRule', () => { tags: ['java', 'java11'], }, }, - 'tags', - 'java', + { + key: 'tags', + value: 'java', + }, ), ).toBe(true); }); @@ -190,7 +208,11 @@ describe('createPropertyRule', () => { describe('toQuery', () => { it('returns an appropriate catalog-backend filter', () => { - expect(toQuery('backstage.io/test-component')).toEqual({ + expect( + toQuery({ + key: 'backstage.io/test-component', + }), + ).toEqual({ key: 'metadata.backstage.io/test-component', }); }); diff --git a/plugins/catalog-backend/src/permissions/rules/createPropertyRule.ts b/plugins/catalog-backend/src/permissions/rules/createPropertyRule.ts index 57de5eb922..19a4423846 100644 --- a/plugins/catalog-backend/src/permissions/rules/createPropertyRule.ts +++ b/plugins/catalog-backend/src/permissions/rules/createPropertyRule.ts @@ -15,16 +15,25 @@ */ import { get } from 'lodash'; -import { Entity } from '@backstage/catalog-model'; import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common'; import { createCatalogPermissionRule } from './util'; +import { z } from 'zod'; export const createPropertyRule = (propertyType: 'metadata' | 'spec') => createCatalogPermissionRule({ name: `HAS_${propertyType.toUpperCase()}`, description: `Allow entities which have the specified ${propertyType} subfield.`, resourceType: RESOURCE_TYPE_CATALOG_ENTITY, - apply: (resource: Entity, key: string, value?: string) => { + paramsSchema: z.object({ + key: z + .string() + .describe(`Property within the entities ${propertyType} to match on`), + value: z + .string() + .optional() + .describe(`Value of the given property to match on`), + }), + apply: (resource, { key, value }) => { const foundValue = get(resource[propertyType], key); if (Array.isArray(foundValue)) { @@ -38,7 +47,7 @@ export const createPropertyRule = (propertyType: 'metadata' | 'spec') => } return !!foundValue; }, - toQuery: (key: string, value?: string) => ({ + toQuery: ({ key, value }) => ({ key: `${propertyType}.${key}`, ...(value !== undefined && { values: [value] }), }), diff --git a/plugins/catalog-backend/src/permissions/rules/hasAnnotation.test.ts b/plugins/catalog-backend/src/permissions/rules/hasAnnotation.test.ts index 609114be3d..73fce20a71 100644 --- a/plugins/catalog-backend/src/permissions/rules/hasAnnotation.test.ts +++ b/plugins/catalog-backend/src/permissions/rules/hasAnnotation.test.ts @@ -31,7 +31,9 @@ describe('hasAnnotation permission rule', () => { }, }, }, - 'backstage.io/test-annotation', + { + annotation: 'backstage.io/test-annotation', + }, ), ).toEqual(false); }); @@ -46,7 +48,9 @@ describe('hasAnnotation permission rule', () => { name: 'test-component', }, }, - 'backstage.io/test-annotation', + { + annotation: 'backstage.io/test-annotation', + }, ), ).toEqual(false); expect( @@ -58,8 +62,10 @@ describe('hasAnnotation permission rule', () => { name: 'test-component', }, }, - 'backstage.io/test-annotation', - 'some value', + { + annotation: 'backstage.io/test-annotation', + value: 'some value', + }, ), ).toEqual(false); }); @@ -78,7 +84,9 @@ describe('hasAnnotation permission rule', () => { }, }, }, - 'backstage.io/test-annotation', + { + annotation: 'backstage.io/test-annotation', + }, ), ).toEqual(true); }); @@ -97,8 +105,10 @@ describe('hasAnnotation permission rule', () => { }, }, }, - 'backstage.io/test-annotation', - 'baz', + { + annotation: 'backstage.io/test-annotation', + value: 'baz', + }, ), ).toEqual(false); }); @@ -117,8 +127,10 @@ describe('hasAnnotation permission rule', () => { }, }, }, - 'backstage.io/test-annotation', - 'bar', + { + annotation: 'backstage.io/test-annotation', + value: 'bar', + }, ), ).toEqual(true); }); @@ -126,7 +138,11 @@ describe('hasAnnotation permission rule', () => { describe('toQuery', () => { it('returns an appropriate catalog-backend filter', () => { - expect(hasAnnotation.toQuery('backstage.io/test-annotation')).toEqual({ + expect( + hasAnnotation.toQuery({ + annotation: 'backstage.io/test-annotation', + }), + ).toEqual({ key: 'metadata.annotations.backstage.io/test-annotation', }); }); @@ -134,7 +150,10 @@ describe('hasAnnotation permission rule', () => { it('returns an appropriate catalog-backend filter with values', () => { expect( - hasAnnotation.toQuery('backstage.io/test-annotation', 'foo'), + hasAnnotation.toQuery({ + annotation: 'backstage.io/test-annotation', + value: 'foo', + }), ).toEqual({ key: 'metadata.annotations.backstage.io/test-annotation', values: ['foo'], diff --git a/plugins/catalog-backend/src/permissions/rules/hasAnnotation.ts b/plugins/catalog-backend/src/permissions/rules/hasAnnotation.ts index 22dbd307a6..c24620f41c 100644 --- a/plugins/catalog-backend/src/permissions/rules/hasAnnotation.ts +++ b/plugins/catalog-backend/src/permissions/rules/hasAnnotation.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common'; +import { z } from 'zod'; import { createCatalogPermissionRule } from './util'; /** @@ -31,12 +31,19 @@ export const hasAnnotation = createCatalogPermissionRule({ description: 'Allow entities which are annotated with the specified annotation', resourceType: RESOURCE_TYPE_CATALOG_ENTITY, - apply: (resource: Entity, annotation: string, value?: string) => + paramsSchema: z.object({ + annotation: z.string().describe('Name of the annotation to match on'), + value: z + .string() + .optional() + .describe('Value of the annotation to match on'), + }), + apply: (resource, { annotation, value }) => !!resource.metadata.annotations?.hasOwnProperty(annotation) && (value === undefined ? true : resource.metadata.annotations?.[annotation] === value), - toQuery: (annotation: string, value?: string) => + toQuery: ({ annotation, value }) => value === undefined ? { key: `metadata.annotations.${annotation}`, diff --git a/plugins/catalog-backend/src/permissions/rules/hasLabel.test.ts b/plugins/catalog-backend/src/permissions/rules/hasLabel.test.ts index a1b9cb5ad0..8aa43402c5 100644 --- a/plugins/catalog-backend/src/permissions/rules/hasLabel.test.ts +++ b/plugins/catalog-backend/src/permissions/rules/hasLabel.test.ts @@ -31,7 +31,9 @@ describe('hasLabel permission rule', () => { }, }, }, - 'backstage.io/testlabel', + { + label: 'backstage.io/testlabel', + }, ), ).toEqual(false); }); @@ -46,7 +48,9 @@ describe('hasLabel permission rule', () => { name: 'test-component', }, }, - 'backstage.io/testlabel', + { + label: 'backstage.io/testlabel', + }, ), ).toEqual(false); }); @@ -65,7 +69,7 @@ describe('hasLabel permission rule', () => { }, }, }, - 'backstage.io/testlabel', + { label: 'backstage.io/testlabel' }, ), ).toEqual(true); }); @@ -73,7 +77,11 @@ describe('hasLabel permission rule', () => { describe('toQuery', () => { it('returns an appropriate catalog-backend filter', () => { - expect(hasLabel.toQuery('backstage.io/testlabel')).toEqual({ + expect( + hasLabel.toQuery({ + label: 'backstage.io/testlabel', + }), + ).toEqual({ key: 'metadata.labels.backstage.io/testlabel', }); }); diff --git a/plugins/catalog-backend/src/permissions/rules/hasLabel.ts b/plugins/catalog-backend/src/permissions/rules/hasLabel.ts index 9f37cbfb59..376534ca6f 100644 --- a/plugins/catalog-backend/src/permissions/rules/hasLabel.ts +++ b/plugins/catalog-backend/src/permissions/rules/hasLabel.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common'; +import { z } from 'zod'; import { createCatalogPermissionRule } from './util'; /** @@ -27,9 +27,12 @@ export const hasLabel = createCatalogPermissionRule({ name: 'HAS_LABEL', description: 'Allow entities which have the specified label metadata.', resourceType: RESOURCE_TYPE_CATALOG_ENTITY, - apply: (resource: Entity, label: string) => + paramsSchema: z.object({ + label: z.string().describe('Name of the label to match one'), + }), + apply: (resource, { label }) => !!resource.metadata.labels?.hasOwnProperty(label), - toQuery: (label: string) => ({ + toQuery: ({ label }) => ({ key: `metadata.labels.${label}`, }), }); diff --git a/plugins/catalog-backend/src/permissions/rules/isEntityKind.test.ts b/plugins/catalog-backend/src/permissions/rules/isEntityKind.test.ts index e11e102728..28983f6bec 100644 --- a/plugins/catalog-backend/src/permissions/rules/isEntityKind.test.ts +++ b/plugins/catalog-backend/src/permissions/rules/isEntityKind.test.ts @@ -27,7 +27,11 @@ describe('isEntityKind', () => { name: 'some-component', }, }; - expect(isEntityKind.apply(component, ['b'])).toBe(true); + expect( + isEntityKind.apply(component, { + kinds: ['b'], + }), + ).toBe(true); }); it('returns false when entity is not the correct kind', () => { @@ -38,13 +42,21 @@ describe('isEntityKind', () => { name: 'some-component', }, }; - expect(isEntityKind.apply(component, ['c'])).toBe(false); + expect( + isEntityKind.apply(component, { + kinds: ['c'], + }), + ).toBe(false); }); }); describe('toQuery', () => { it('returns an appropriate catalog-backend filter', () => { - expect(isEntityKind.toQuery(['b'])).toEqual({ + expect( + isEntityKind.toQuery({ + kinds: ['b'], + }), + ).toEqual({ key: 'kind', values: ['b'], }); diff --git a/plugins/catalog-backend/src/permissions/rules/isEntityKind.ts b/plugins/catalog-backend/src/permissions/rules/isEntityKind.ts index 3bd600e8fe..eee7a790b2 100644 --- a/plugins/catalog-backend/src/permissions/rules/isEntityKind.ts +++ b/plugins/catalog-backend/src/permissions/rules/isEntityKind.ts @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common'; +import { z } from 'zod'; import { EntitiesSearchFilter } from '../../catalog/types'; import { createCatalogPermissionRule } from './util'; @@ -27,11 +27,16 @@ export const isEntityKind = createCatalogPermissionRule({ name: 'IS_ENTITY_KIND', description: 'Allow entities with the specified kind', resourceType: RESOURCE_TYPE_CATALOG_ENTITY, - apply(resource: Entity, kinds: string[]) { + paramsSchema: z.object({ + kinds: z + .array(z.string()) + .describe('List of kinds to match at least one of'), + }), + apply(resource, { kinds }) { const resourceKind = resource.kind.toLocaleLowerCase('en-US'); return kinds.some(kind => kind.toLocaleLowerCase('en-US') === resourceKind); }, - toQuery(kinds: string[]): EntitiesSearchFilter { + toQuery({ kinds }): EntitiesSearchFilter { return { key: 'kind', values: kinds.map(kind => kind.toLocaleLowerCase('en-US')), diff --git a/plugins/catalog-backend/src/permissions/rules/isEntityOwner.test.ts b/plugins/catalog-backend/src/permissions/rules/isEntityOwner.test.ts index 11304c768c..f09e484c32 100644 --- a/plugins/catalog-backend/src/permissions/rules/isEntityOwner.test.ts +++ b/plugins/catalog-backend/src/permissions/rules/isEntityOwner.test.ts @@ -33,9 +33,11 @@ describe('isEntityOwner', () => { }, ], }; - expect(isEntityOwner.apply(component, ['user:default/spiderman'])).toBe( - true, - ); + expect( + isEntityOwner.apply(component, { + claims: ['user:default/spiderman'], + }), + ).toBe(true); }); it('returns false when entity is not owned by the given user', () => { @@ -52,9 +54,11 @@ describe('isEntityOwner', () => { }, ], }; - expect(isEntityOwner.apply(component, ['user:default/spiderman'])).toBe( - false, - ); + expect( + isEntityOwner.apply(component, { + claims: ['user:default/spiderman'], + }), + ).toBe(false); }); it('returns false when entity does not have an owner', () => { @@ -65,15 +69,21 @@ describe('isEntityOwner', () => { name: 'some-component', }, }; - expect(isEntityOwner.apply(component, ['user:default/spiderman'])).toBe( - false, - ); + expect( + isEntityOwner.apply(component, { + claims: ['user:default/spiderman'], + }), + ).toBe(false); }); }); describe('toQuery', () => { it('returns an appropriate catalog-backend filter', () => { - expect(isEntityOwner.toQuery(['user:default/spiderman'])).toEqual({ + expect( + isEntityOwner.toQuery({ + claims: ['user:default/spiderman'], + }), + ).toEqual({ key: 'relations.ownedBy', values: ['user:default/spiderman'], }); diff --git a/plugins/catalog-backend/src/permissions/rules/isEntityOwner.ts b/plugins/catalog-backend/src/permissions/rules/isEntityOwner.ts index 5403da2310..c3bfff5c2e 100644 --- a/plugins/catalog-backend/src/permissions/rules/isEntityOwner.ts +++ b/plugins/catalog-backend/src/permissions/rules/isEntityOwner.ts @@ -14,8 +14,9 @@ * limitations under the License. */ -import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model'; +import { RELATION_OWNED_BY } from '@backstage/catalog-model'; import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common'; +import { z } from 'zod'; import { createCatalogPermissionRule } from './util'; /** @@ -28,7 +29,14 @@ export const isEntityOwner = createCatalogPermissionRule({ name: 'IS_ENTITY_OWNER', description: 'Allow entities owned by the current user', resourceType: RESOURCE_TYPE_CATALOG_ENTITY, - apply: (resource: Entity, claims: string[]) => { + paramsSchema: z.object({ + claims: z + .array(z.string()) + .describe( + `List of claims to match at least one on within ${RELATION_OWNED_BY}`, + ), + }), + apply: (resource, { claims }) => { if (!resource.relations) { return false; } @@ -37,7 +45,7 @@ export const isEntityOwner = createCatalogPermissionRule({ .filter(relation => relation.type === RELATION_OWNED_BY) .some(relation => claims.includes(relation.targetRef)); }, - toQuery: (claims: string[]) => ({ + toQuery: ({ claims }) => ({ key: 'relations.ownedBy', values: claims, }), diff --git a/plugins/catalog-backend/src/permissions/rules/util.ts b/plugins/catalog-backend/src/permissions/rules/util.ts index 36b351595d..ea25e115d2 100644 --- a/plugins/catalog-backend/src/permissions/rules/util.ts +++ b/plugins/catalog-backend/src/permissions/rules/util.ts @@ -16,6 +16,7 @@ import { Entity } from '@backstage/catalog-model'; import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common'; +import { PermissionRuleParams } from '@backstage/plugin-permission-common'; import { makeCreatePermissionRule, PermissionRule, @@ -29,8 +30,9 @@ import { EntitiesSearchFilter } from '../../catalog/types'; * * @alpha */ -export type CatalogPermissionRule = - PermissionRule; +export type CatalogPermissionRule< + TParams extends PermissionRuleParams = PermissionRuleParams, +> = PermissionRule; /** * Helper function for creating correctly-typed diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts index b15d1df4bb..66764dc89f 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts @@ -65,7 +65,7 @@ describe('AuthorizedEntitiesCatalog', () => { fakePermissionApi.authorizeConditional.mockResolvedValue([ { result: AuthorizeResult.CONDITIONAL, - conditions: { rule: 'IS_ENTITY_KIND', params: [['b']] }, + conditions: { rule: 'IS_ENTITY_KIND', params: { kinds: ['b'] } }, }, ]); const catalog = createCatalog(isEntityKind); @@ -117,7 +117,7 @@ describe('AuthorizedEntitiesCatalog', () => { fakePermissionApi.authorizeConditional.mockResolvedValue([ { result: AuthorizeResult.CONDITIONAL, - conditions: { rule: 'IS_ENTITY_KIND', params: [['b']] }, + conditions: { rule: 'IS_ENTITY_KIND', params: { kinds: ['b'] } }, }, ]); fakeCatalog.entities.mockResolvedValue({ entities: [] }); @@ -136,7 +136,7 @@ describe('AuthorizedEntitiesCatalog', () => { fakePermissionApi.authorizeConditional.mockResolvedValue([ { result: AuthorizeResult.CONDITIONAL, - conditions: { rule: 'IS_ENTITY_KIND', params: [['b']] }, + conditions: { rule: 'IS_ENTITY_KIND', params: { kinds: ['b'] } }, }, ]); fakeCatalog.entities.mockResolvedValue({ @@ -272,7 +272,7 @@ describe('AuthorizedEntitiesCatalog', () => { fakePermissionApi.authorizeConditional.mockResolvedValue([ { result: AuthorizeResult.CONDITIONAL, - conditions: { rule: 'IS_ENTITY_KIND', params: [['b']] }, + conditions: { rule: 'IS_ENTITY_KIND', params: { kinds: ['b'] } }, }, ]); const catalog = createCatalog(isEntityKind); diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index d47ace1454..d5bc7057dd 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -56,7 +56,7 @@ import { yamlPlaceholderResolver, } from '../modules/core/PlaceholderProcessor'; import { defaultEntityDataParser } from '../modules/util/parse'; -import { LocationAnalyzer } from '../ingestion/types'; +import { LocationAnalyzer, ScmLocationAnalyzer } from '../ingestion/types'; import { CatalogProcessingEngine } from '../processing'; import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; import { applyDatabaseMigrations } from '../database/migrations'; @@ -115,6 +115,10 @@ export type CatalogEnvironment = { * after the processors' pre-processing steps. All policies are given the * chance to inspect the entity, and all of them have to pass in order for * the entity to be considered valid from an overall point of view. + * - Location analyzers can be added. These are responsible for analyzing + * repositories when onboarding them into the catalog, by finding + * catalog-info.yaml files and other artifacts that can help automatically + * register or create catalog data on the user's behalf. * - Placeholder resolvers can be replaced or added. These run on the raw * structured data between the parsing and pre-processing steps, to replace * dollar-prefixed entries with their actual values (like $file). @@ -135,6 +139,7 @@ export class CatalogBuilder { private fieldFormatValidators: Partial; private entityProviders: EntityProvider[]; private processors: CatalogProcessor[]; + private locationAnalyzers: ScmLocationAnalyzer[]; private processorsReplace: boolean; private parser: CatalogProcessorParser | undefined; private onProcessingError?: (event: { @@ -165,6 +170,7 @@ export class CatalogBuilder { this.fieldFormatValidators = {}; this.entityProviders = []; this.processors = []; + this.locationAnalyzers = []; this.processorsReplace = false; this.parser = undefined; this.permissionRules = Object.values(catalogPermissionRules); @@ -335,6 +341,21 @@ export class CatalogBuilder { ]; } + /** + * Adds Location Analyzers. These are responsible for analyzing + * repositories when onboarding them into the catalog, by finding + * catalog-info.yaml files and other artifacts that can help automatically + * register or create catalog data on the user's behalf. + * + * @param locationAnalyzers - One or more location analyzers + */ + addLocationAnalyzers( + ...analyzers: Array> + ): CatalogBuilder { + this.locationAnalyzers.push(...analyzers.flat()); + return this; + } + /** * Sets up the catalog to use a custom parser for entity data. * @@ -478,7 +499,8 @@ export class CatalogBuilder { ); const locationAnalyzer = - this.locationAnalyzer ?? new RepoLocationAnalyzer(logger, integrations); + this.locationAnalyzer ?? + new RepoLocationAnalyzer(logger, integrations, this.locationAnalyzers); const locationService = new AuthorizedLocationService( new DefaultLocationService(locationStore, orchestrator, { allowedLocationTypes: this.allowedLocationType, diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index 16a36eb8c4..f383df7364 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -36,6 +36,7 @@ import { } from '@backstage/plugin-permission-node'; import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common'; import { CatalogProcessingOrchestrator } from '../processing/types'; +import { z } from 'zod'; describe('createRouter readonly disabled', () => { let entitiesCatalog: jest.Mocked; @@ -695,6 +696,9 @@ describe('NextRouter permissioning', () => { name: 'FAKE_RULE', description: 'fake rule', resourceType: RESOURCE_TYPE_CATALOG_ENTITY, + paramsSchema: z.object({ + foo: z.string(), + }), apply: () => true, toQuery: () => ({ key: '', values: [] }), }); @@ -758,7 +762,9 @@ describe('NextRouter permissioning', () => { conditions: { rule: 'FAKE_RULE', resourceType: 'catalog-entity', - params: ['user:default/spiderman'], + params: { + foo: 'user:default/spiderman', + }, }, }, ], diff --git a/plugins/catalog-backend/src/service/createRouter.ts b/plugins/catalog-backend/src/service/createRouter.ts index 0002231bab..5267519c33 100644 --- a/plugins/catalog-backend/src/service/createRouter.ts +++ b/plugins/catalog-backend/src/service/createRouter.ts @@ -229,9 +229,15 @@ export async function createRouter( router.post('/analyze-location', async (req, res) => { const body = await validateRequestBody( req, - z.object({ location: locationInput }), + z.object({ + location: locationInput, + catalogFilename: z.string().optional(), + }), ); - const schema = z.object({ location: locationInput }); + const schema = z.object({ + location: locationInput, + catalogFilename: z.string().optional(), + }); const output = await locationAnalyzer.analyzeLocation(schema.parse(body)); res.status(200).json(output); }); diff --git a/plugins/catalog-common/CHANGELOG.md b/plugins/catalog-common/CHANGELOG.md index 04fb82c760..2ecbca904f 100644 --- a/plugins/catalog-common/CHANGELOG.md +++ b/plugins/catalog-common/CHANGELOG.md @@ -1,5 +1,39 @@ # @backstage/plugin-catalog-common +## 1.0.7 + +### Patch Changes + +- 823acaa88b: Moved the following types from `@backstage/plugin-catalog-backend` to this package. + + - AnalyzeLocationResponse + - AnalyzeLocationRequest + - AnalyzeLocationExistingEntity + - AnalyzeLocationGenerateEntity + - AnalyzeLocationEntityField + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-permission-common@0.7.0 + - @backstage/plugin-search-common@1.1.0 + +## 1.0.7-next.2 + +### Patch Changes + +- 823acaa88b: Moved the following types from `@backstage/plugin-catalog-backend` to this package. + + - AnalyzeLocationResponse + - AnalyzeLocationRequest + - AnalyzeLocationExistingEntity + - AnalyzeLocationGenerateEntity + - AnalyzeLocationEntityField + +- Updated dependencies + - @backstage/plugin-permission-common@0.7.0-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + ## 1.0.7-next.1 ### Patch Changes diff --git a/plugins/catalog-common/api-report.md b/plugins/catalog-common/api-report.md index b7eaf99d8b..1bef626c79 100644 --- a/plugins/catalog-common/api-report.md +++ b/plugins/catalog-common/api-report.md @@ -4,9 +4,46 @@ ```ts import { BasicPermission } from '@backstage/plugin-permission-common'; +import { Entity } from '@backstage/catalog-model'; import { IndexableDocument } from '@backstage/plugin-search-common'; import { ResourcePermission } from '@backstage/plugin-permission-common'; +// @public (undocumented) +export type AnalyzeLocationEntityField = { + field: string; + state: + | 'analysisSuggestedValue' + | 'analysisSuggestedNoValue' + | 'needsUserInput'; + value: string | null; + description: string; +}; + +// @public +export type AnalyzeLocationExistingEntity = { + location: LocationSpec; + isRegistered: boolean; + entity: Entity; +}; + +// @public +export type AnalyzeLocationGenerateEntity = { + entity: RecursivePartial; + fields: AnalyzeLocationEntityField[]; +}; + +// @public (undocumented) +export type AnalyzeLocationRequest = { + location: LocationSpec; + catalogFilename?: string; +}; + +// @public (undocumented) +export type AnalyzeLocationResponse = { + existingEntityFiles: AnalyzeLocationExistingEntity[]; + generateEntities: AnalyzeLocationGenerateEntity[]; +}; + // @alpha export const catalogEntityCreatePermission: BasicPermission; @@ -55,6 +92,13 @@ export const catalogPermissions: ( | ResourcePermission<'catalog-entity'> )[]; +// @public +export type LocationSpec = { + type: string; + target: string; + presence?: 'optional' | 'required'; +}; + // @alpha export const RESOURCE_TYPE_CATALOG_ENTITY = 'catalog-entity'; ``` diff --git a/plugins/catalog-common/package.json b/plugins/catalog-common/package.json index 5be665b7d6..b700002f9c 100644 --- a/plugins/catalog-common/package.json +++ b/plugins/catalog-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-common", "description": "Common functionalities for the catalog plugin", - "version": "1.0.7-next.1", + "version": "1.0.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,6 +33,7 @@ "clean": "backstage-cli package clean" }, "dependencies": { + "@backstage/catalog-model": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", "@backstage/plugin-search-common": "workspace:^" }, diff --git a/plugins/catalog-common/src/common.ts b/plugins/catalog-common/src/common.ts new file mode 100644 index 0000000000..c926a94b28 --- /dev/null +++ b/plugins/catalog-common/src/common.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2022 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. + */ + +/** + * Holds the entity location information. + * + * @remarks + * + * `presence` flag: when using repo importer plugin, location is being created before the component yaml file is merged to the main branch. + * This flag is then set to indicate that the file can be not present. + * default value: 'required'. + * + * @public + */ +export type LocationSpec = { + type: string; + target: string; + presence?: 'optional' | 'required'; +}; diff --git a/plugins/catalog-common/src/index.ts b/plugins/catalog-common/src/index.ts index 614948e56c..95bd65bc92 100644 --- a/plugins/catalog-common/src/index.ts +++ b/plugins/catalog-common/src/index.ts @@ -35,3 +35,5 @@ export { export type { CatalogEntityPermission } from './permissions'; export * from './search'; +export * from './ingestion'; +export type { LocationSpec } from './common'; diff --git a/plugins/catalog-common/src/ingestion/LocationAnalyzer.ts b/plugins/catalog-common/src/ingestion/LocationAnalyzer.ts new file mode 100644 index 0000000000..08ae5ef0cb --- /dev/null +++ b/plugins/catalog-common/src/ingestion/LocationAnalyzer.ts @@ -0,0 +1,88 @@ +/* + * Copyright 2022 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 { LocationSpec } from '../common'; +import { Entity } from '@backstage/catalog-model'; +import { RecursivePartial } from './RecursivePartial'; + +/** @public */ +export type AnalyzeLocationRequest = { + location: LocationSpec; + catalogFilename?: string; +}; + +/** @public */ +export type AnalyzeLocationResponse = { + existingEntityFiles: AnalyzeLocationExistingEntity[]; + generateEntities: AnalyzeLocationGenerateEntity[]; +}; + +/** + * If the folder pointed to already contained catalog info yaml files, they are + * read and emitted like this so that the frontend can inform the user that it + * located them and can make sure to register them as well if they weren't + * already + * @public + */ +export type AnalyzeLocationExistingEntity = { + location: LocationSpec; + isRegistered: boolean; + entity: Entity; +}; + +/** + * This is some form of representation of what the analyzer could deduce. + * We should probably have a chat about how this can best be conveyed to + * the frontend. It'll probably contain a (possibly incomplete) entity, plus + * enough info for the frontend to know what form data to show to the user + * for overriding/completing the info. + * @public + */ +export type AnalyzeLocationGenerateEntity = { + // Some form of partial representation of the entity + entity: RecursivePartial; + // Lists the suggestions that the user may want to override + fields: AnalyzeLocationEntityField[]; +}; + +// This is where I get really vague. Something like this perhaps? Or it could be +// something like a json-schema that contains enough info for the frontend to +// be able to present a form and explanations +/** @public */ +export type AnalyzeLocationEntityField = { + /** + * e.g. "spec.owner"? The frontend needs to know how to "inject" the field into the + * entity again if the user wants to change it + */ + field: string; + + /** The outcome of the analysis for this particular field */ + state: + | 'analysisSuggestedValue' + | 'analysisSuggestedNoValue' + | 'needsUserInput'; + + // If the analysis did suggest a value, this is where it would be. Not sure if we want + // to limit this to strings or if we want it to be any JsonValue + value: string | null; + /** + * A text to show to the user to inform about the choices made. Like, it could say + * "Found a CODEOWNERS file that covers this target, so we suggest leaving this + * field empty; which would currently make it owned by X" where X is taken from the + * codeowners file. + */ + description: string; +}; diff --git a/plugins/catalog-common/src/ingestion/RecursivePartial.test.ts b/plugins/catalog-common/src/ingestion/RecursivePartial.test.ts new file mode 100644 index 0000000000..ab8d50534e --- /dev/null +++ b/plugins/catalog-common/src/ingestion/RecursivePartial.test.ts @@ -0,0 +1,31 @@ +/* + * 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 { RecursivePartial } from './RecursivePartial'; + +describe('RecursivePartial', () => { + it('is recursive', () => { + type X = { + required: { + required: string; + }; + }; + const x: RecursivePartial = { + required: {}, + }; + expect(x).toEqual({ required: {} }); + }); +}); diff --git a/plugins/catalog-common/src/ingestion/RecursivePartial.ts b/plugins/catalog-common/src/ingestion/RecursivePartial.ts new file mode 100644 index 0000000000..c452836f34 --- /dev/null +++ b/plugins/catalog-common/src/ingestion/RecursivePartial.ts @@ -0,0 +1,27 @@ +/* + * 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. + */ + +/** + * Makes all keys of an entire hierarchy optional. + * @ignore + */ +export type RecursivePartial = { + [P in keyof T]?: T[P] extends (infer U)[] + ? RecursivePartial[] + : T[P] extends object + ? RecursivePartial + : T[P]; +}; diff --git a/plugins/catalog-common/src/ingestion/index.ts b/plugins/catalog-common/src/ingestion/index.ts new file mode 100644 index 0000000000..aced124bb4 --- /dev/null +++ b/plugins/catalog-common/src/ingestion/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2022 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 type { + AnalyzeLocationResponse, + AnalyzeLocationRequest, + AnalyzeLocationExistingEntity, + AnalyzeLocationGenerateEntity, + AnalyzeLocationEntityField, +} from './LocationAnalyzer'; diff --git a/plugins/catalog-customized/CHANGELOG.md b/plugins/catalog-customized/CHANGELOG.md index 8ba2f88dce..3f659df9b3 100644 --- a/plugins/catalog-customized/CHANGELOG.md +++ b/plugins/catalog-customized/CHANGELOG.md @@ -1,5 +1,21 @@ # @internal/plugin-catalog-customized +## 0.0.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/plugin-catalog@1.6.0 + +## 0.0.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.6.0-next.2 + - @backstage/plugin-catalog-react@1.2.0-next.2 + ## 0.0.3-next.1 ### Patch Changes diff --git a/plugins/catalog-customized/package.json b/plugins/catalog-customized/package.json index ddd80bee43..bb5564c135 100644 --- a/plugins/catalog-customized/package.json +++ b/plugins/catalog-customized/package.json @@ -1,7 +1,7 @@ { "name": "@internal/plugin-catalog-customized", "description": "The internal Backstage Customizable plugin for browsing the Backstage catalog", - "version": "0.0.3-next.1", + "version": "0.0.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index 724a4bf1a2..a6a2f5770b 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-catalog-graph +## 0.2.22 + +### Patch Changes + +- bde1e8c8e2: Added `curve` prop to the `DependencyGraph` component to select the type of layout +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/catalog-client@1.1.1 + - @backstage/core-plugin-api@1.0.7 + - @backstage/theme@0.2.16 + +## 0.2.22-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/catalog-client@1.1.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/theme@0.2.16 + ## 0.2.22-next.1 ### Patch Changes diff --git a/plugins/catalog-graph/api-report.md b/plugins/catalog-graph/api-report.md index ed4ca2a2c9..95f497fe95 100644 --- a/plugins/catalog-graph/api-report.md +++ b/plugins/catalog-graph/api-report.md @@ -30,6 +30,7 @@ export const CatalogGraphPage: (props: { mergeRelations?: boolean | undefined; direction?: Direction | undefined; showFilters?: boolean | undefined; + curve?: 'curveStepBefore' | 'curveMonotoneX' | undefined; } | undefined; }) => JSX.Element; @@ -120,6 +121,7 @@ export const EntityRelationsGraph: (props: { renderLabel?: | DependencyGraphTypes.RenderLabelFunction | undefined; + curve?: 'curveStepBefore' | 'curveMonotoneX' | undefined; }) => JSX.Element; // @public diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 8fd71845c2..ccfbd5160e 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.2.22-next.1", + "version": "0.2.22", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx index 8e59c127d9..a6a6c2a711 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx @@ -40,6 +40,7 @@ import { EntityRelationsGraph, RelationPairs, } from '../EntityRelationsGraph'; +import { CurveFilter } from './CurveFilter'; import { DirectionFilter } from './DirectionFilter'; import { MaxDepthFilter } from './MaxDepthFilter'; import { SelectedKindsFilter } from './SelectedKindsFilter'; @@ -110,6 +111,7 @@ export const CatalogGraphPage = (props: { mergeRelations?: boolean; direction?: Direction; showFilters?: boolean; + curve?: 'curveStepBefore' | 'curveMonotoneX'; }; }) => { const { relationPairs = ALL_RELATION_PAIRS, initialState } = props; @@ -130,6 +132,8 @@ export const CatalogGraphPage = (props: { setMergeRelations, direction, setDirection, + curve, + setCurve, rootEntityNames, setRootEntityNames, showFilters, @@ -201,6 +205,7 @@ export const CatalogGraphPage = (props: { relationPairs={relationPairs} /> + diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx new file mode 100644 index 0000000000..0be0fca66b --- /dev/null +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx @@ -0,0 +1,47 @@ +/* + * 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 { render, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import { CurveFilter } from './CurveFilter'; + +describe('', () => { + test('should display current curve label', () => { + const onChange = jest.fn(); + const { getByText } = render( + , + ); + + expect(getByText('Monotone X')).toBeInTheDocument(); + }); + + test('should select an alternative curve factory', async () => { + const onChange = jest.fn(); + const { getByText, getByTestId } = render( + , + ); + + expect(getByText('Step Before')).toBeInTheDocument(); + + await userEvent.click(getByTestId('select')); + await userEvent.click(getByText('Monotone X')); + + await waitFor(() => { + expect(getByText('Monotone X')).toBeInTheDocument(); + expect(onChange).toHaveBeenCalledWith('curveMonotoneX'); + }); + }); +}); diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.tsx new file mode 100644 index 0000000000..6cda334cf1 --- /dev/null +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.tsx @@ -0,0 +1,49 @@ +/* + * 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 { Select } from '@backstage/core-components'; +import { Box } from '@material-ui/core'; +import React, { useCallback } from 'react'; + +type Curve = 'curveStepBefore' | 'curveMonotoneX'; +const CURVE_DISPLAY_NAMES: Record = { + curveMonotoneX: 'Monotone X', + curveStepBefore: 'Step Before', +}; + +export type Props = { + value: Curve; + onChange: (value: 'curveStepBefore' | 'curveMonotoneX') => void; +}; + +const curves: Array = ['curveMonotoneX', 'curveStepBefore']; + +export const CurveFilter = ({ value, onChange }: Props) => { + const handleChange = useCallback(v => onChange(v as Curve), [onChange]); + + return ( + +