diff --git a/.changeset/big-spies-stare.md b/.changeset/big-spies-stare.md
new file mode 100644
index 0000000000..bab1643284
--- /dev/null
+++ b/.changeset/big-spies-stare.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-techdocs-backend': patch
+---
+
+Dedicated token for techdocs cache sync
diff --git a/.changeset/blue-pumas-cheer.md b/.changeset/blue-pumas-cheer.md
deleted file mode 100644
index f6cbf180ff..0000000000
--- a/.changeset/blue-pumas-cheer.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/create-app': patch
----
-
-Added the Kubernetes plugin to `create-app`
diff --git a/.changeset/bright-trainers-brake.md b/.changeset/bright-trainers-brake.md
deleted file mode 100644
index 2299d83a32..0000000000
--- a/.changeset/bright-trainers-brake.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-Add frontend-dynamic-container role to eslint config factory
diff --git a/.changeset/brown-actors-clap.md b/.changeset/brown-actors-clap.md
new file mode 100644
index 0000000000..af25a6e87f
--- /dev/null
+++ b/.changeset/brown-actors-clap.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-plugin-api': minor
+---
+
+**BREAKING**: The deprecated identity and token manager services have been removed. This means that `coreServices.identity` and `coreServices.tokenManager` are gone, along with related types and utilities in other packages.
diff --git a/.changeset/brown-boxes-arrive.md b/.changeset/brown-boxes-arrive.md
new file mode 100644
index 0000000000..edefc0dcaf
--- /dev/null
+++ b/.changeset/brown-boxes-arrive.md
@@ -0,0 +1,5 @@
+---
+'@backstage/frontend-plugin-api': minor
+---
+
+**BREAKING**: Removed support for "v1" extensions. This means that it is no longer possible to declare inputs and outputs as objects when using `createExtension`. In addition, all extension creators except for `createComponentExtension` have been removed, use the equivalent blueprint instead. See the [1.30 migration documentation](https://backstage.io/docs/frontend-system/architecture/migrations/#130) for more information on this change.
diff --git a/.changeset/brown-worms-sneeze.md b/.changeset/brown-worms-sneeze.md
new file mode 100644
index 0000000000..51359dd384
--- /dev/null
+++ b/.changeset/brown-worms-sneeze.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-techdocs-react': patch
+---
+
+Fixed issue in useShadowRootElements which could lead to unlimited render loops
diff --git a/.changeset/chilly-trains-sleep.md b/.changeset/chilly-trains-sleep.md
deleted file mode 100644
index cde7600c8b..0000000000
--- a/.changeset/chilly-trains-sleep.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/plugin-events-backend-module-aws-sqs': patch
-'@backstage/plugin-catalog-backend-module-aws': patch
-'@backstage/backend-common': patch
----
-
-Setup user agent header for AWS sdk clients, this enables users to better track API calls made from Backstage to AWS APIs through things like CloudTrail.
diff --git a/.changeset/cold-ways-protect.md b/.changeset/cold-ways-protect.md
new file mode 100644
index 0000000000..26b693fcc6
--- /dev/null
+++ b/.changeset/cold-ways-protect.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-dynamic-feature-service': patch
+---
+
+Relax type check for a plugin's default export to also accept a BackendFeature defined as a function instead of an object
diff --git a/.changeset/cool-insects-remember.md b/.changeset/cool-insects-remember.md
deleted file mode 100644
index ab33015377..0000000000
--- a/.changeset/cool-insects-remember.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-backend-module-notifications': patch
----
-
-Add examples for notification:send scaffolder action & improve related tests
diff --git a/.changeset/cool-poems-hammer.md b/.changeset/cool-poems-hammer.md
new file mode 100644
index 0000000000..fd5897343e
--- /dev/null
+++ b/.changeset/cool-poems-hammer.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-search-react': patch
+---
+
+Slight type tweak to match newer React versions better
diff --git a/.changeset/cuddly-rocks-invent.md b/.changeset/cuddly-rocks-invent.md
new file mode 100644
index 0000000000..c4ce8ebdb9
--- /dev/null
+++ b/.changeset/cuddly-rocks-invent.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-dynamic-feature-service': minor
+---
+
+**BREAKING**: `dynamicPluginsServiceFactory` is no longer callable as a function. If you need to provide options to make a custom factory, use `dynamicPluginsSchemasServiceFactoryWithOptions` instead.
diff --git a/.changeset/curly-brooms-raise.md b/.changeset/curly-brooms-raise.md
new file mode 100644
index 0000000000..84c8892f7c
--- /dev/null
+++ b/.changeset/curly-brooms-raise.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-search-backend': patch
+---
+
+Deprecate create router as the legacy backend system will no longer be supported.
diff --git a/.changeset/dry-monkeys-mate.md b/.changeset/dry-monkeys-mate.md
new file mode 100644
index 0000000000..51883ddefa
--- /dev/null
+++ b/.changeset/dry-monkeys-mate.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-kubernetes-backend': patch
+---
+
+The `KubernetesBuilder` and its related types has been marked as deprecared. This backend should instead be initialized using the new backend system.
diff --git a/.changeset/dry-squids-tap.md b/.changeset/dry-squids-tap.md
deleted file mode 100644
index 613ec4b7cb..0000000000
--- a/.changeset/dry-squids-tap.md
+++ /dev/null
@@ -1,31 +0,0 @@
----
-'@backstage/frontend-plugin-api': patch
----
-
-Introduce a new way to encapsulate extension kinds that replaces the extension creator pattern with `createExtensionBlueprint`
-
-This allows the creation of extension instances with the following pattern:
-
-```tsx
-// create the extension blueprint which is used to create instances
-const EntityCardBlueprint = createExtensionBlueprint({
- kind: 'entity-card',
- attachTo: { id: 'test', input: 'default' },
- output: {
- element: coreExtensionData.reactElement,
- },
- factory(params: { text: string }) {
- return {
- element:
{params.text}
,
- };
- },
-});
-
-// create an instance of the extension blueprint with params
-const testExtension = EntityCardBlueprint.make({
- name: 'foo',
- params: {
- text: 'Hello World',
- },
-});
-```
diff --git a/.changeset/early-trees-dance.md b/.changeset/early-trees-dance.md
deleted file mode 100644
index 55b2c08f89..0000000000
--- a/.changeset/early-trees-dance.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/frontend-plugin-api': patch
----
-
-The `ExtensionBoundary` now by default infers whether it's routable from whether it outputs a route path.
diff --git a/.changeset/eighty-tables-hope.md b/.changeset/eighty-tables-hope.md
new file mode 100644
index 0000000000..203cc8bb65
--- /dev/null
+++ b/.changeset/eighty-tables-hope.md
@@ -0,0 +1,5 @@
+---
+'@backstage/frontend-test-utils': patch
+---
+
+Internal update to add support for passing an `ApiRegistry` when creating the node tree
diff --git a/.changeset/empty-coats-sparkle.md b/.changeset/empty-coats-sparkle.md
new file mode 100644
index 0000000000..7e3b791c6b
--- /dev/null
+++ b/.changeset/empty-coats-sparkle.md
@@ -0,0 +1,5 @@
+---
+'@backstage/frontend-test-utils': minor
+---
+
+Removed support for testing "v1" extensions, where outputs are defined as an object rather than an array.
diff --git a/.changeset/friendly-feet-refuse.md b/.changeset/friendly-feet-refuse.md
deleted file mode 100644
index 677d21d48a..0000000000
--- a/.changeset/friendly-feet-refuse.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-backend-module-rails': patch
----
-
-Add examples for fetch:rails scaffolder action & improve related tests
diff --git a/.changeset/fuzzy-spies-share.md b/.changeset/fuzzy-spies-share.md
new file mode 100644
index 0000000000..99108b81a0
--- /dev/null
+++ b/.changeset/fuzzy-spies-share.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-signals-backend': patch
+---
+
+The `createRouter` and its related types has been marked as deprecared. This backend should instead be initialized using the new backend system.
diff --git a/.changeset/giant-jars-mix.md b/.changeset/giant-jars-mix.md
new file mode 100644
index 0000000000..652e23a280
--- /dev/null
+++ b/.changeset/giant-jars-mix.md
@@ -0,0 +1,6 @@
+---
+'@backstage/create-app': patch
+'@backstage/plugin-devtools': patch
+---
+
+Minor dockerfile syntax update
diff --git a/.changeset/grumpy-owls-suffer.md b/.changeset/grumpy-owls-suffer.md
deleted file mode 100644
index 977055eb33..0000000000
--- a/.changeset/grumpy-owls-suffer.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-backend-module-gitlab': patch
----
-
-Allow the `createGitlabProjectVariableAction` to use oauth tokens
diff --git a/.changeset/healthy-moons-drum.md b/.changeset/healthy-moons-drum.md
new file mode 100644
index 0000000000..0f61d0b141
--- /dev/null
+++ b/.changeset/healthy-moons-drum.md
@@ -0,0 +1,9 @@
+---
+'@backstage/backend-plugin-api': minor
+---
+
+Removed the following deprecated exports
+
+- `BackendPluginConfig` use `CreateBackendPluginOptions`
+- `BackendModuleConfig` use `CreateBackendModuleOptions`
+- `ExtensionPointConfig` use `CreateExtensionPointOptions`
diff --git a/.changeset/hip-hairs-exist.md b/.changeset/hip-hairs-exist.md
deleted file mode 100644
index d8cb939dfb..0000000000
--- a/.changeset/hip-hairs-exist.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/plugin-catalog-backend': patch
-'@backstage/plugin-catalog-node': patch
----
-
-Added setAllowedLocationTypes while introducing a new extension point called CatalogLocationsExtensionPoint
diff --git a/.changeset/hip-pandas-think.md b/.changeset/hip-pandas-think.md
new file mode 100644
index 0000000000..a43349db63
--- /dev/null
+++ b/.changeset/hip-pandas-think.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-techdocs-node': patch
+---
+
+Fix typo and unify TechDocs casing in doc strings
diff --git a/.changeset/kind-moose-learn.md b/.changeset/kind-moose-learn.md
new file mode 100644
index 0000000000..810064b1a1
--- /dev/null
+++ b/.changeset/kind-moose-learn.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-app-api': minor
+---
+
+**BREAKING**: The deprecated `identityServiceFactory` and `tokenManagerServiceFactory` have been removed.
diff --git a/.changeset/kind-walls-speak.md b/.changeset/kind-walls-speak.md
new file mode 100644
index 0000000000..441e247dea
--- /dev/null
+++ b/.changeset/kind-walls-speak.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-defaults': minor
+---
+
+**BREAKING**: The backwards compatibility with plugins using legacy auth through the token manager service has been removed. This means that instead of falling back to using the old token manager, requests towards plugins that don't support the new auth system will simply fail. Please make sure that all plugins in your deployment are hosted within a backend instance from the new backend system.
diff --git a/.changeset/large-poets-check.md b/.changeset/large-poets-check.md
new file mode 100644
index 0000000000..393d26ac9b
--- /dev/null
+++ b/.changeset/large-poets-check.md
@@ -0,0 +1,11 @@
+---
+'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch
+'@backstage/plugin-search-backend-module-techdocs': patch
+'@backstage/plugin-search-backend-module-catalog': patch
+'@backstage/plugin-search-backend-module-explore': patch
+'@backstage/plugin-permission-node': patch
+'@backstage/plugin-signals-backend': patch
+'@backstage/plugin-auth-backend': patch
+---
+
+Internal refactor to remove dependencies on the identity and token manager services, which have been removed. Public APIs no longer require the identity service or token manager to be provided.
diff --git a/.changeset/late-games-protect.md b/.changeset/late-games-protect.md
deleted file mode 100644
index 584780f836..0000000000
--- a/.changeset/late-games-protect.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-react': minor
----
-
-Add scaffolder option to display object items in separate rows on review page
diff --git a/.changeset/little-bulldogs-guess.md b/.changeset/little-bulldogs-guess.md
deleted file mode 100644
index 3e646a033f..0000000000
--- a/.changeset/little-bulldogs-guess.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-The experimental module federation build now has the ability to force the use of development versions of `react` and `react-dom` by setting the `FORCE_REACT_DEVELOPMENT` flag.
diff --git a/.changeset/little-suns-fly.md b/.changeset/little-suns-fly.md
deleted file mode 100644
index 887df4be6d..0000000000
--- a/.changeset/little-suns-fly.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend-module-msgraph': patch
----
-
-Added option to ingest groups based on their group membership in Azure Entra ID
diff --git a/.changeset/lucky-sheep-cover.md b/.changeset/lucky-sheep-cover.md
new file mode 100644
index 0000000000..9b9420a73a
--- /dev/null
+++ b/.changeset/lucky-sheep-cover.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-common': patch
+---
+
+The `legacyPlugin` and `makeLegacyPlugin` helpers now provide their own shim implementation of the identity and token manager services, as these services are being removed from the new backend system.
diff --git a/.changeset/metal-garlics-fetch.md b/.changeset/metal-garlics-fetch.md
new file mode 100644
index 0000000000..076b5f34a5
--- /dev/null
+++ b/.changeset/metal-garlics-fetch.md
@@ -0,0 +1,6 @@
+---
+'@backstage/frontend-plugin-api': patch
+'@backstage/frontend-app-api': patch
+---
+
+Moved several implementations of built-in APIs from being hardcoded in the app to instead be provided as API extensions. This moves all API-related inputs from the `app` extension to the respective API extensions. For example, extensions created with `ThemeBlueprint` are now attached to the `themes` input of `api:app-theme` rather than the `app` extension.
diff --git a/.changeset/mighty-days-kiss.md b/.changeset/mighty-days-kiss.md
new file mode 100644
index 0000000000..f78e602e25
--- /dev/null
+++ b/.changeset/mighty-days-kiss.md
@@ -0,0 +1,6 @@
+---
+'@backstage/backend-defaults': patch
+'@backstage/backend-app-api': patch
+---
+
+`auth.externalAccess` should be optional in the config schema
diff --git a/.changeset/mighty-dolls-retire.md b/.changeset/mighty-dolls-retire.md
deleted file mode 100644
index b31c443a38..0000000000
--- a/.changeset/mighty-dolls-retire.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-react': patch
----
-
-Internal refactor to remove unnecessary `routable` prop in the implementation of the `createEntityContentExtension` alpha export.
diff --git a/.changeset/modern-parrots-protect.md b/.changeset/modern-parrots-protect.md
deleted file mode 100644
index bb4df2c248..0000000000
--- a/.changeset/modern-parrots-protect.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-react': patch
----
-
-support `ajv-errors` for scaffolder validation to allow for customizing the error messages
diff --git a/.changeset/nasty-tigers-knock.md b/.changeset/nasty-tigers-knock.md
new file mode 100644
index 0000000000..c48d725779
--- /dev/null
+++ b/.changeset/nasty-tigers-knock.md
@@ -0,0 +1,37 @@
+---
+'@backstage/backend-common': patch
+'@backstage/backend-dynamic-feature-service': patch
+'@backstage/plugin-app-backend': patch
+'@backstage/plugin-catalog-backend-module-aws': patch
+'@backstage/plugin-catalog-backend-module-azure': patch
+'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch
+'@backstage/plugin-catalog-backend-module-bitbucket-server': patch
+'@backstage/plugin-catalog-backend-module-gerrit': patch
+'@backstage/plugin-catalog-backend-module-github': patch
+'@backstage/plugin-catalog-backend-module-gitlab': patch
+'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch
+'@backstage/plugin-catalog-backend-module-msgraph': patch
+'@backstage/plugin-catalog-backend-module-puppetdb': patch
+'@backstage/plugin-catalog-backend': patch
+'@backstage/plugin-events-backend-module-aws-sqs': patch
+'@backstage/plugin-events-backend-module-azure': patch
+'@backstage/plugin-events-backend-module-bitbucket-cloud': patch
+'@backstage/plugin-events-backend-module-gerrit': patch
+'@backstage/plugin-events-backend-module-github': patch
+'@backstage/plugin-events-backend-module-gitlab': patch
+'@backstage/plugin-events-backend': patch
+'@backstage/plugin-kubernetes-backend': patch
+'@backstage/plugin-permission-backend': patch
+'@backstage/plugin-proxy-backend': patch
+'@backstage/plugin-scaffolder-backend': patch
+'@backstage/plugin-search-backend-module-catalog': patch
+'@backstage/plugin-search-backend-module-elasticsearch': patch
+'@backstage/plugin-search-backend-module-explore': patch
+'@backstage/plugin-search-backend-module-pg': patch
+'@backstage/plugin-search-backend-module-techdocs': patch
+'@backstage/plugin-search-backend': patch
+'@backstage/plugin-techdocs-backend': patch
+'@backstage/plugin-user-settings-backend': patch
+---
+
+Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature.
diff --git a/.changeset/new-poets-unite.md b/.changeset/new-poets-unite.md
new file mode 100644
index 0000000000..83172ffd66
--- /dev/null
+++ b/.changeset/new-poets-unite.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-kubernetes': patch
+---
+
+Make k8s entity content appear on components & resources only by default in new FE system
diff --git a/.changeset/nice-peas-retire.md b/.changeset/nice-peas-retire.md
deleted file mode 100644
index 7b7b13706d..0000000000
--- a/.changeset/nice-peas-retire.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/integration': minor
-'@backstage/backend-defaults': patch
----
-
-Updated `GitlabUrlReader.readUrl` and `GitlabUrlReader.readTree` to accept a user-provided token, supporting both bearer and private tokens.
diff --git a/.changeset/nine-cherries-decide.md b/.changeset/nine-cherries-decide.md
deleted file mode 100644
index b20c24aa85..0000000000
--- a/.changeset/nine-cherries-decide.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-'@backstage/backend-common': patch
----
-
-The remaining exports in the package have now been deprecated:
-
-- `cacheToPluginCacheManager`
-- `createLegacyAuthAdapters`
-- `LegacyCreateRouter`
-- `legacyPlugin`
-- `loggerToWinstonLogger`
-- `makeLegacyPlugin`
-
-Users of these export should fully [migrate to the new backend system](https://backstage.io/docs/backend-system/building-backends/migrating).
diff --git a/.changeset/ninety-mayflies-raise.md b/.changeset/ninety-mayflies-raise.md
new file mode 100644
index 0000000000..62042c2615
--- /dev/null
+++ b/.changeset/ninety-mayflies-raise.md
@@ -0,0 +1,6 @@
+---
+'@backstage/plugin-search-backend-module-elasticsearch': patch
+'@backstage/plugin-search-backend-module-pg': patch
+---
+
+Internal refactor to use `LoggerService` and `DatabaseService` instead of the legacy `Logger` and `PluginDatabaseManager` types.
diff --git a/.changeset/pink-gorillas-brake.md b/.changeset/pink-gorillas-brake.md
deleted file mode 100644
index 188884f13c..0000000000
--- a/.changeset/pink-gorillas-brake.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-react': patch
----
-
-Fix extra divider displayed on user list picker component
diff --git a/.changeset/popular-cooks-camp.md b/.changeset/popular-cooks-camp.md
new file mode 100644
index 0000000000..4049825efd
--- /dev/null
+++ b/.changeset/popular-cooks-camp.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-test-utils': minor
+---
+
+**BREAKING**: Removed service mocks for the identity and token manager services, which have been removed from `@backstage/backend-plugin-api`.
diff --git a/.changeset/pre.json b/.changeset/pre.json
index be1b694c9e..f5e95fffc7 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -2,190 +2,191 @@
"mode": "pre",
"tag": "next",
"initialVersions": {
- "example-app": "0.2.99",
- "@backstage/app-defaults": "1.5.8",
- "example-app-next": "0.0.13",
- "app-next-example-plugin": "0.0.13",
- "example-backend": "0.0.28",
- "@backstage/backend-app-api": "0.8.0",
- "@backstage/backend-common": "0.23.3",
- "@backstage/backend-defaults": "0.4.0",
- "@backstage/backend-dev-utils": "0.1.4",
- "@backstage/backend-dynamic-feature-service": "0.2.15",
- "example-backend-legacy": "0.2.100",
- "@backstage/backend-openapi-utils": "0.1.15",
- "@backstage/backend-plugin-api": "0.7.0",
- "@backstage/backend-tasks": "0.5.27",
- "@backstage/backend-test-utils": "0.4.4",
- "@backstage/catalog-client": "1.6.5",
- "@backstage/catalog-model": "1.5.0",
- "@backstage/cli": "0.26.11",
+ "example-app": "0.2.100",
+ "@backstage/app-defaults": "1.5.10",
+ "example-app-next": "0.0.14",
+ "app-next-example-plugin": "0.0.14",
+ "example-backend": "0.0.29",
+ "@backstage/backend-app-api": "0.9.0",
+ "@backstage/backend-common": "0.24.0",
+ "@backstage/backend-defaults": "0.4.2",
+ "@backstage/backend-dev-utils": "0.1.5",
+ "@backstage/backend-dynamic-feature-service": "0.3.0",
+ "example-backend-legacy": "0.2.101",
+ "@backstage/backend-openapi-utils": "0.1.16",
+ "@backstage/backend-plugin-api": "0.8.0",
+ "@backstage/backend-tasks": "0.6.0",
+ "@backstage/backend-test-utils": "0.5.0",
+ "@backstage/catalog-client": "1.6.6",
+ "@backstage/catalog-model": "1.6.0",
+ "@backstage/cli": "0.27.0",
"@backstage/cli-common": "0.1.14",
"@backstage/cli-node": "0.2.7",
"@backstage/codemods": "0.1.49",
"@backstage/config": "1.2.0",
- "@backstage/config-loader": "1.8.1",
- "@backstage/core-app-api": "1.14.0",
- "@backstage/core-compat-api": "0.2.7",
- "@backstage/core-components": "0.14.9",
+ "@backstage/config-loader": "1.9.0",
+ "@backstage/core-app-api": "1.14.2",
+ "@backstage/core-compat-api": "0.2.8",
+ "@backstage/core-components": "0.14.10",
"@backstage/core-plugin-api": "1.9.3",
- "@backstage/create-app": "0.5.17",
- "@backstage/dev-utils": "1.0.35",
- "e2e-test": "0.2.18",
+ "@backstage/create-app": "0.5.18",
+ "@backstage/dev-utils": "1.0.37",
+ "e2e-test": "0.2.19",
"@backstage/e2e-test-utils": "0.1.1",
"@backstage/errors": "1.2.4",
"@backstage/eslint-plugin": "0.1.8",
- "@backstage/frontend-app-api": "0.7.3",
- "@backstage/frontend-plugin-api": "0.6.7",
- "@backstage/frontend-test-utils": "0.1.10",
- "@backstage/integration": "1.13.0",
+ "@backstage/frontend-app-api": "0.8.0",
+ "@backstage/frontend-plugin-api": "0.7.0",
+ "@backstage/frontend-test-utils": "0.1.12",
+ "@backstage/integration": "1.14.0",
"@backstage/integration-aws-node": "0.1.12",
- "@backstage/integration-react": "1.1.29",
+ "@backstage/integration-react": "1.1.30",
"@backstage/release-manifests": "0.0.11",
- "@backstage/repo-tools": "0.9.4",
- "@techdocs/cli": "1.8.16",
- "techdocs-cli-embedded-app": "0.2.98",
- "@backstage/test-utils": "1.5.8",
+ "@backstage/repo-tools": "0.9.5",
+ "@techdocs/cli": "1.8.17",
+ "techdocs-cli-embedded-app": "0.2.99",
+ "@backstage/test-utils": "1.5.10",
"@backstage/theme": "0.5.6",
"@backstage/types": "1.1.1",
"@backstage/version-bridge": "1.0.8",
"yarn-plugin-backstage": "0.0.1",
- "@backstage/plugin-api-docs": "0.11.7",
+ "@backstage/plugin-api-docs": "0.11.8",
"@backstage/plugin-api-docs-module-protoc-gen-doc": "0.1.7",
- "@backstage/plugin-app-backend": "0.3.71",
- "@backstage/plugin-app-node": "0.1.22",
- "@backstage/plugin-app-visualizer": "0.1.8",
- "@backstage/plugin-auth-backend": "0.22.9",
- "@backstage/plugin-auth-backend-module-atlassian-provider": "0.2.3",
- "@backstage/plugin-auth-backend-module-aws-alb-provider": "0.1.14",
- "@backstage/plugin-auth-backend-module-azure-easyauth-provider": "0.1.5",
- "@backstage/plugin-auth-backend-module-bitbucket-provider": "0.1.5",
- "@backstage/plugin-auth-backend-module-cloudflare-access-provider": "0.1.5",
- "@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.2.17",
- "@backstage/plugin-auth-backend-module-github-provider": "0.1.19",
- "@backstage/plugin-auth-backend-module-gitlab-provider": "0.1.19",
- "@backstage/plugin-auth-backend-module-google-provider": "0.1.19",
- "@backstage/plugin-auth-backend-module-guest-provider": "0.1.8",
- "@backstage/plugin-auth-backend-module-microsoft-provider": "0.1.17",
- "@backstage/plugin-auth-backend-module-oauth2-provider": "0.2.3",
- "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.1.15",
- "@backstage/plugin-auth-backend-module-oidc-provider": "0.2.3",
- "@backstage/plugin-auth-backend-module-okta-provider": "0.0.15",
- "@backstage/plugin-auth-backend-module-onelogin-provider": "0.1.3",
- "@backstage/plugin-auth-backend-module-pinniped-provider": "0.1.16",
- "@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.2.3",
- "@backstage/plugin-auth-node": "0.4.17",
- "@backstage/plugin-auth-react": "0.1.4",
- "@backstage/plugin-bitbucket-cloud-common": "0.2.21",
- "@backstage/plugin-catalog": "1.21.1",
- "@backstage/plugin-catalog-backend": "1.24.0",
- "@backstage/plugin-catalog-backend-module-aws": "0.3.17",
- "@backstage/plugin-catalog-backend-module-azure": "0.1.42",
- "@backstage/plugin-catalog-backend-module-backstage-openapi": "0.2.5",
- "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.2.9",
- "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.1.36",
- "@backstage/plugin-catalog-backend-module-gcp": "0.1.23",
- "@backstage/plugin-catalog-backend-module-gerrit": "0.1.39",
- "@backstage/plugin-catalog-backend-module-github": "0.6.5",
- "@backstage/plugin-catalog-backend-module-github-org": "0.1.17",
- "@backstage/plugin-catalog-backend-module-gitlab": "0.3.21",
- "@backstage/plugin-catalog-backend-module-gitlab-org": "0.0.5",
- "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.4.27",
- "@backstage/plugin-catalog-backend-module-ldap": "0.7.0",
- "@backstage/plugin-catalog-backend-module-logs": "0.0.1",
- "@backstage/plugin-catalog-backend-module-msgraph": "0.5.30",
- "@backstage/plugin-catalog-backend-module-openapi": "0.1.40",
- "@backstage/plugin-catalog-backend-module-puppetdb": "0.1.28",
- "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.1.20",
- "@backstage/plugin-catalog-backend-module-unprocessed": "0.4.9",
- "@backstage/plugin-catalog-common": "1.0.25",
- "@backstage/plugin-catalog-graph": "0.4.7",
- "@backstage/plugin-catalog-import": "0.12.1",
- "@backstage/plugin-catalog-node": "1.12.4",
- "@backstage/plugin-catalog-react": "1.12.2",
- "@backstage/plugin-catalog-unprocessed-entities": "0.2.6",
- "@backstage/plugin-catalog-unprocessed-entities-common": "0.0.3",
- "@backstage/plugin-config-schema": "0.1.57",
- "@backstage/plugin-devtools": "0.1.16",
- "@backstage/plugin-devtools-backend": "0.3.8",
- "@backstage/plugin-devtools-common": "0.1.11",
- "@backstage/plugin-events-backend": "0.3.9",
- "@backstage/plugin-events-backend-module-aws-sqs": "0.3.8",
- "@backstage/plugin-events-backend-module-azure": "0.2.8",
- "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.2.8",
- "@backstage/plugin-events-backend-module-gerrit": "0.2.8",
- "@backstage/plugin-events-backend-module-github": "0.2.8",
- "@backstage/plugin-events-backend-module-gitlab": "0.2.8",
- "@backstage/plugin-events-backend-test-utils": "0.1.32",
- "@backstage/plugin-events-node": "0.3.8",
- "@internal/plugin-todo-list": "1.0.29",
- "@internal/plugin-todo-list-backend": "1.0.29",
- "@internal/plugin-todo-list-common": "1.0.20",
- "@backstage/plugin-home": "0.7.7",
- "@backstage/plugin-home-react": "0.1.15",
- "@backstage/plugin-kubernetes": "0.11.12",
- "@backstage/plugin-kubernetes-backend": "0.18.3",
- "@backstage/plugin-kubernetes-cluster": "0.0.13",
- "@backstage/plugin-kubernetes-common": "0.8.1",
- "@backstage/plugin-kubernetes-node": "0.1.16",
- "@backstage/plugin-kubernetes-react": "0.4.1",
- "@backstage/plugin-notifications": "0.2.3",
- "@backstage/plugin-notifications-backend": "0.3.3",
- "@backstage/plugin-notifications-backend-module-email": "0.1.3",
+ "@backstage/plugin-app-backend": "0.3.72",
+ "@backstage/plugin-app-node": "0.1.23",
+ "@backstage/plugin-app-visualizer": "0.1.9",
+ "@backstage/plugin-auth-backend": "0.22.10",
+ "@backstage/plugin-auth-backend-module-atlassian-provider": "0.2.4",
+ "@backstage/plugin-auth-backend-module-aws-alb-provider": "0.1.15",
+ "@backstage/plugin-auth-backend-module-azure-easyauth-provider": "0.1.6",
+ "@backstage/plugin-auth-backend-module-bitbucket-provider": "0.1.6",
+ "@backstage/plugin-auth-backend-module-cloudflare-access-provider": "0.2.0",
+ "@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.2.18",
+ "@backstage/plugin-auth-backend-module-github-provider": "0.1.20",
+ "@backstage/plugin-auth-backend-module-gitlab-provider": "0.1.20",
+ "@backstage/plugin-auth-backend-module-google-provider": "0.1.20",
+ "@backstage/plugin-auth-backend-module-guest-provider": "0.1.9",
+ "@backstage/plugin-auth-backend-module-microsoft-provider": "0.1.18",
+ "@backstage/plugin-auth-backend-module-oauth2-provider": "0.2.4",
+ "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.1.16",
+ "@backstage/plugin-auth-backend-module-oidc-provider": "0.2.4",
+ "@backstage/plugin-auth-backend-module-okta-provider": "0.0.16",
+ "@backstage/plugin-auth-backend-module-onelogin-provider": "0.1.4",
+ "@backstage/plugin-auth-backend-module-pinniped-provider": "0.1.17",
+ "@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.2.4",
+ "@backstage/plugin-auth-node": "0.5.0",
+ "@backstage/plugin-auth-react": "0.1.5",
+ "@backstage/plugin-bitbucket-cloud-common": "0.2.22",
+ "@backstage/plugin-catalog": "1.22.0",
+ "@backstage/plugin-catalog-backend": "1.25.0",
+ "@backstage/plugin-catalog-backend-module-aws": "0.4.0",
+ "@backstage/plugin-catalog-backend-module-azure": "0.2.0",
+ "@backstage/plugin-catalog-backend-module-backstage-openapi": "0.3.0",
+ "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.3.0",
+ "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.2.0",
+ "@backstage/plugin-catalog-backend-module-gcp": "0.2.0",
+ "@backstage/plugin-catalog-backend-module-gerrit": "0.2.0",
+ "@backstage/plugin-catalog-backend-module-github": "0.7.0",
+ "@backstage/plugin-catalog-backend-module-github-org": "0.2.0",
+ "@backstage/plugin-catalog-backend-module-gitlab": "0.4.0",
+ "@backstage/plugin-catalog-backend-module-gitlab-org": "0.1.0",
+ "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.5.0",
+ "@backstage/plugin-catalog-backend-module-ldap": "0.8.0",
+ "@backstage/plugin-catalog-backend-module-logs": "0.0.2",
+ "@backstage/plugin-catalog-backend-module-msgraph": "0.6.0",
+ "@backstage/plugin-catalog-backend-module-openapi": "0.1.41",
+ "@backstage/plugin-catalog-backend-module-puppetdb": "0.2.0",
+ "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.1.21",
+ "@backstage/plugin-catalog-backend-module-unprocessed": "0.4.10",
+ "@backstage/plugin-catalog-common": "1.0.26",
+ "@backstage/plugin-catalog-graph": "0.4.8",
+ "@backstage/plugin-catalog-import": "0.12.2",
+ "@backstage/plugin-catalog-node": "1.12.5",
+ "@backstage/plugin-catalog-react": "1.12.3",
+ "@backstage/plugin-catalog-unprocessed-entities": "0.2.7",
+ "@backstage/plugin-catalog-unprocessed-entities-common": "0.0.4",
+ "@backstage/plugin-config-schema": "0.1.58",
+ "@backstage/plugin-devtools": "0.1.17",
+ "@backstage/plugin-devtools-backend": "0.3.9",
+ "@backstage/plugin-devtools-common": "0.1.12",
+ "@backstage/plugin-events-backend": "0.3.10",
+ "@backstage/plugin-events-backend-module-aws-sqs": "0.4.0",
+ "@backstage/plugin-events-backend-module-azure": "0.2.9",
+ "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.2.9",
+ "@backstage/plugin-events-backend-module-gerrit": "0.2.9",
+ "@backstage/plugin-events-backend-module-github": "0.2.9",
+ "@backstage/plugin-events-backend-module-gitlab": "0.2.9",
+ "@backstage/plugin-events-backend-test-utils": "0.1.33",
+ "@backstage/plugin-events-node": "0.3.9",
+ "@internal/plugin-todo-list": "1.0.30",
+ "@internal/plugin-todo-list-backend": "1.0.30",
+ "@internal/plugin-todo-list-common": "1.0.21",
+ "@backstage/plugin-home": "0.7.9",
+ "@backstage/plugin-home-react": "0.1.16",
+ "@backstage/plugin-kubernetes": "0.11.13",
+ "@backstage/plugin-kubernetes-backend": "0.18.4",
+ "@backstage/plugin-kubernetes-cluster": "0.0.14",
+ "@backstage/plugin-kubernetes-common": "0.8.2",
+ "@backstage/plugin-kubernetes-node": "0.1.17",
+ "@backstage/plugin-kubernetes-react": "0.4.2",
+ "@backstage/plugin-notifications": "0.3.0",
+ "@backstage/plugin-notifications-backend": "0.3.4",
+ "@backstage/plugin-notifications-backend-module-email": "0.2.0",
"@backstage/plugin-notifications-common": "0.0.5",
- "@backstage/plugin-notifications-node": "0.2.3",
- "@backstage/plugin-org": "0.6.27",
- "@backstage/plugin-org-react": "0.1.26",
- "@backstage/plugin-permission-backend": "0.5.46",
- "@backstage/plugin-permission-backend-module-allow-all-policy": "0.1.19",
- "@backstage/plugin-permission-common": "0.8.0",
- "@backstage/plugin-permission-node": "0.8.0",
- "@backstage/plugin-permission-react": "0.4.24",
- "@backstage/plugin-proxy-backend": "0.5.3",
- "@backstage/plugin-scaffolder": "1.23.0",
- "@backstage/plugin-scaffolder-backend": "1.23.0",
- "@backstage/plugin-scaffolder-backend-module-azure": "0.1.14",
- "@backstage/plugin-scaffolder-backend-module-bitbucket": "0.2.12",
- "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "0.1.12",
- "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "0.1.12",
- "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.2.23",
- "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.46",
- "@backstage/plugin-scaffolder-backend-module-gcp": "0.1.0",
- "@backstage/plugin-scaffolder-backend-module-gerrit": "0.1.14",
- "@backstage/plugin-scaffolder-backend-module-gitea": "0.1.12",
- "@backstage/plugin-scaffolder-backend-module-github": "0.4.0",
- "@backstage/plugin-scaffolder-backend-module-gitlab": "0.4.4",
- "@backstage/plugin-scaffolder-backend-module-notifications": "0.0.5",
- "@backstage/plugin-scaffolder-backend-module-rails": "0.4.39",
- "@backstage/plugin-scaffolder-backend-module-sentry": "0.1.30",
- "@backstage/plugin-scaffolder-backend-module-yeoman": "0.3.6",
- "@backstage/plugin-scaffolder-common": "1.5.4",
- "@backstage/plugin-scaffolder-node": "0.4.8",
- "@backstage/plugin-scaffolder-node-test-utils": "0.1.9",
- "@backstage/plugin-scaffolder-react": "1.10.0",
- "@backstage/plugin-search": "1.4.14",
- "@backstage/plugin-search-backend": "1.5.14",
- "@backstage/plugin-search-backend-module-catalog": "0.1.28",
- "@backstage/plugin-search-backend-module-elasticsearch": "1.5.3",
- "@backstage/plugin-search-backend-module-explore": "0.1.28",
- "@backstage/plugin-search-backend-module-pg": "0.5.32",
- "@backstage/plugin-search-backend-module-stack-overflow-collator": "0.1.15",
- "@backstage/plugin-search-backend-module-techdocs": "0.1.27",
- "@backstage/plugin-search-backend-node": "1.2.27",
- "@backstage/plugin-search-common": "1.2.13",
- "@backstage/plugin-search-react": "1.7.13",
- "@backstage/plugin-signals": "0.0.8",
- "@backstage/plugin-signals-backend": "0.1.8",
- "@backstage/plugin-signals-node": "0.1.8",
+ "@backstage/plugin-notifications-node": "0.2.4",
+ "@backstage/plugin-org": "0.6.28",
+ "@backstage/plugin-org-react": "0.1.27",
+ "@backstage/plugin-permission-backend": "0.5.47",
+ "@backstage/plugin-permission-backend-module-allow-all-policy": "0.1.20",
+ "@backstage/plugin-permission-common": "0.8.1",
+ "@backstage/plugin-permission-node": "0.8.1",
+ "@backstage/plugin-permission-react": "0.4.25",
+ "@backstage/plugin-proxy-backend": "0.5.4",
+ "@backstage/plugin-scaffolder": "1.24.0",
+ "@backstage/plugin-scaffolder-backend": "1.24.0",
+ "@backstage/plugin-scaffolder-backend-module-azure": "0.1.15",
+ "@backstage/plugin-scaffolder-backend-module-bitbucket": "0.2.13",
+ "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "0.1.13",
+ "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "0.1.13",
+ "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.2.24",
+ "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.47",
+ "@backstage/plugin-scaffolder-backend-module-gcp": "0.1.1",
+ "@backstage/plugin-scaffolder-backend-module-gerrit": "0.1.15",
+ "@backstage/plugin-scaffolder-backend-module-gitea": "0.1.13",
+ "@backstage/plugin-scaffolder-backend-module-github": "0.4.1",
+ "@backstage/plugin-scaffolder-backend-module-gitlab": "0.4.5",
+ "@backstage/plugin-scaffolder-backend-module-notifications": "0.0.6",
+ "@backstage/plugin-scaffolder-backend-module-rails": "0.4.40",
+ "@backstage/plugin-scaffolder-backend-module-sentry": "0.1.31",
+ "@backstage/plugin-scaffolder-backend-module-yeoman": "0.3.7",
+ "@backstage/plugin-scaffolder-common": "1.5.5",
+ "@backstage/plugin-scaffolder-node": "0.4.9",
+ "@backstage/plugin-scaffolder-node-test-utils": "0.1.10",
+ "@backstage/plugin-scaffolder-react": "1.11.0",
+ "@backstage/plugin-search": "1.4.15",
+ "@backstage/plugin-search-backend": "1.5.15",
+ "@backstage/plugin-search-backend-module-catalog": "0.2.0",
+ "@backstage/plugin-search-backend-module-elasticsearch": "1.5.4",
+ "@backstage/plugin-search-backend-module-explore": "0.2.0",
+ "@backstage/plugin-search-backend-module-pg": "0.5.33",
+ "@backstage/plugin-search-backend-module-stack-overflow-collator": "0.2.0",
+ "@backstage/plugin-search-backend-module-techdocs": "0.2.0",
+ "@backstage/plugin-search-backend-node": "1.3.0",
+ "@backstage/plugin-search-common": "1.2.14",
+ "@backstage/plugin-search-react": "1.7.14",
+ "@backstage/plugin-signals": "0.0.9",
+ "@backstage/plugin-signals-backend": "0.1.9",
+ "@backstage/plugin-signals-node": "0.1.9",
"@backstage/plugin-signals-react": "0.0.4",
- "@backstage/plugin-techdocs": "1.10.7",
- "@backstage/plugin-techdocs-addons-test-utils": "1.0.35",
- "@backstage/plugin-techdocs-backend": "1.10.9",
- "@backstage/plugin-techdocs-module-addons-contrib": "1.1.12",
- "@backstage/plugin-techdocs-node": "1.12.8",
- "@backstage/plugin-techdocs-react": "1.2.6",
- "@backstage/plugin-user-settings": "0.8.9",
- "@backstage/plugin-user-settings-backend": "0.2.21",
+ "@backstage/plugin-techdocs": "1.10.8",
+ "@backstage/plugin-techdocs-addons-test-utils": "1.0.37",
+ "@backstage/plugin-techdocs-backend": "1.10.10",
+ "@backstage/plugin-techdocs-common": "0.1.0",
+ "@backstage/plugin-techdocs-module-addons-contrib": "1.1.13",
+ "@backstage/plugin-techdocs-node": "1.12.9",
+ "@backstage/plugin-techdocs-react": "1.2.7",
+ "@backstage/plugin-user-settings": "0.8.11",
+ "@backstage/plugin-user-settings-backend": "0.2.22",
"@backstage/plugin-user-settings-common": "0.0.1"
},
"changesets": []
diff --git a/.changeset/purple-cows-sing.md b/.changeset/purple-cows-sing.md
new file mode 100644
index 0000000000..3391ced648
--- /dev/null
+++ b/.changeset/purple-cows-sing.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-plugin-api': minor
+---
+
+Removed deprecated `ServiceFactoryOrFunction` type.
diff --git a/.changeset/purple-glasses-tease.md b/.changeset/purple-glasses-tease.md
new file mode 100644
index 0000000000..5a107c1a2b
--- /dev/null
+++ b/.changeset/purple-glasses-tease.md
@@ -0,0 +1,11 @@
+---
+'@backstage/plugin-search-backend-module-techdocs': patch
+'@backstage/plugin-search-backend-module-catalog': patch
+'@backstage/plugin-search-backend-module-explore': patch
+---
+
+The following collator factories are deprecated, please [migrate](https://backstage.io/docs/backend-system/building-backends/migrating) to the new backend system and follow the instructions below to install collators via module:
+
+- `DefaultCatalogCollatorFactory`: https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-catalog/README.md#installation;
+- `ToolDocumentCollatorFactory`: https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-explore/README.md#installation;
+- `DefaultTechDocsCollatorFactory`: https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-techdocs/README.md#installation.
diff --git a/.changeset/rare-feet-melt.md b/.changeset/rare-feet-melt.md
new file mode 100644
index 0000000000..b28b09a29d
--- /dev/null
+++ b/.changeset/rare-feet-melt.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-user-settings-backend': patch
+---
+
+Replaced usage of the deprecated identity service with the new HTTP auth service for the new backend system.
diff --git a/.changeset/selfish-bees-think.md b/.changeset/selfish-bees-think.md
deleted file mode 100644
index 15598ea551..0000000000
--- a/.changeset/selfish-bees-think.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/backend-defaults': patch
----
-
-Fixed the routing of the new health check service, the health endpoints should now properly be available at `/.backstage/health/v1/readiness` and `/.backstage/health/v1/liveness`.
diff --git a/.changeset/serious-cheetahs-help.md b/.changeset/serious-cheetahs-help.md
new file mode 100644
index 0000000000..88bad696c5
--- /dev/null
+++ b/.changeset/serious-cheetahs-help.md
@@ -0,0 +1,7 @@
+---
+'@backstage/plugin-catalog-react': patch
+'@backstage/plugin-search-react': patch
+'@backstage/plugin-catalog': patch
+---
+
+The `/alpha` export no longer export extension creators for the new frontend system, existing usage should be switched to use the equivalent extension blueprint instead. For more information see the [new frontend system 1.30 migration documentation](https://backstage.io/docs/frontend-system/architecture/migrations#130).
diff --git a/.changeset/sharp-fans-tan.md b/.changeset/sharp-fans-tan.md
new file mode 100644
index 0000000000..add4a85758
--- /dev/null
+++ b/.changeset/sharp-fans-tan.md
@@ -0,0 +1,8 @@
+---
+'@backstage/plugin-scaffolder-react': patch
+---
+
+- Fix secret widget field not displaying as required.
+- Fix secret widget not able to be required inside nested objects.
+- Fix secret widget not able to be disabled.
+- Support `minLength` and `maxLength` properties for secret widget.
diff --git a/.changeset/sharp-mayflies-beg.md b/.changeset/sharp-mayflies-beg.md
new file mode 100644
index 0000000000..c78335c187
--- /dev/null
+++ b/.changeset/sharp-mayflies-beg.md
@@ -0,0 +1,5 @@
+---
+'@backstage/frontend-app-api': patch
+---
+
+Internal refactor following removal of v1 extension support. The app implementation itself still supports v1 extensions at runtime.
diff --git a/.changeset/short-moles-brush.md b/.changeset/short-moles-brush.md
new file mode 100644
index 0000000000..033805c2b5
--- /dev/null
+++ b/.changeset/short-moles-brush.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder-backend': minor
+---
+
+`createRouter` and its related types has been marked as deprecared. This backend should instead be initialized using the new backend system.
diff --git a/.changeset/silent-eyes-lie.md b/.changeset/silent-eyes-lie.md
new file mode 100644
index 0000000000..e9da3d2685
--- /dev/null
+++ b/.changeset/silent-eyes-lie.md
@@ -0,0 +1,5 @@
+---
+'@backstage/test-utils': minor
+---
+
+Added the icons option to the renderInTestApp function's TestAppOptions to be forwarded to the icons option of `createApp`.
diff --git a/.changeset/six-goats-sort.md b/.changeset/six-goats-sort.md
new file mode 100644
index 0000000000..5df8924066
--- /dev/null
+++ b/.changeset/six-goats-sort.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog-backend-module-gitlab': patch
+---
+
+Remove dependency on backend-common
diff --git a/.changeset/six-humans-guess.md b/.changeset/six-humans-guess.md
new file mode 100644
index 0000000000..60845257c8
--- /dev/null
+++ b/.changeset/six-humans-guess.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-proxy-backend': patch
+---
+
+Deprecated `createRouter` and its router options in favour of the new backend system.
diff --git a/.changeset/sixty-rabbits-cheat.md b/.changeset/sixty-rabbits-cheat.md
new file mode 100644
index 0000000000..0161e272ed
--- /dev/null
+++ b/.changeset/sixty-rabbits-cheat.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-search-react': minor
+---
+
+Make use of the `useApp` hook to retrieve the specified search icon in the SearchBar
diff --git a/.changeset/spicy-vans-eat.md b/.changeset/spicy-vans-eat.md
new file mode 100644
index 0000000000..037a5f18e0
--- /dev/null
+++ b/.changeset/spicy-vans-eat.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-app-api': patch
+---
+
+Fix feature loaders in CJS double-default nested builds
diff --git a/.changeset/stupid-dots-relate.md b/.changeset/stupid-dots-relate.md
deleted file mode 100644
index a83777bff9..0000000000
--- a/.changeset/stupid-dots-relate.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch
----
-
-Add examples for `fetch:cookiecutter` scaffolder action & improve related tests
diff --git a/.changeset/swift-garlics-mix.md b/.changeset/swift-garlics-mix.md
new file mode 100644
index 0000000000..fc0993be3f
--- /dev/null
+++ b/.changeset/swift-garlics-mix.md
@@ -0,0 +1,63 @@
+---
+'@backstage/backend-app-api': minor
+'@backstage/backend-common': minor
+'@backstage/backend-defaults': minor
+'@backstage/backend-plugin-api': minor
+'@backstage/backend-test-utils': minor
+'@backstage/plugin-auth-backend-module-atlassian-provider': minor
+'@backstage/plugin-auth-backend-module-aws-alb-provider': minor
+'@backstage/plugin-auth-backend-module-azure-easyauth-provider': minor
+'@backstage/plugin-auth-backend-module-bitbucket-provider': minor
+'@backstage/plugin-auth-backend-module-cloudflare-access-provider': minor
+'@backstage/plugin-auth-backend-module-gcp-iap-provider': minor
+'@backstage/plugin-auth-backend-module-github-provider': minor
+'@backstage/plugin-auth-backend-module-gitlab-provider': minor
+'@backstage/plugin-auth-backend-module-google-provider': minor
+'@backstage/plugin-auth-backend-module-guest-provider': minor
+'@backstage/plugin-auth-backend-module-microsoft-provider': minor
+'@backstage/plugin-auth-backend-module-oauth2-provider': minor
+'@backstage/plugin-auth-backend-module-oauth2-proxy-provider': minor
+'@backstage/plugin-auth-backend-module-oidc-provider': minor
+'@backstage/plugin-auth-backend-module-okta-provider': minor
+'@backstage/plugin-auth-backend-module-onelogin-provider': minor
+'@backstage/plugin-auth-backend-module-pinniped-provider': minor
+'@backstage/plugin-auth-backend-module-vmware-cloud-provider': minor
+'@backstage/plugin-auth-backend': minor
+'@backstage/plugin-catalog-backend-module-backstage-openapi': minor
+'@backstage/plugin-catalog-backend-module-gcp': minor
+'@backstage/plugin-catalog-backend-module-github-org': minor
+'@backstage/plugin-catalog-backend-module-gitlab-org': minor
+'@backstage/plugin-catalog-backend-module-ldap': minor
+'@backstage/plugin-catalog-backend-module-logs': minor
+'@backstage/plugin-catalog-backend-module-openapi': minor
+'@backstage/plugin-catalog-backend-module-scaffolder-entity-model': minor
+'@backstage/plugin-catalog-backend-module-unprocessed': minor
+'@backstage/plugin-devtools-backend': minor
+'@backstage/plugin-events-node': minor
+'@backstage/plugin-notifications-backend-module-email': minor
+'@backstage/plugin-notifications-backend': minor
+'@backstage/plugin-permission-backend-module-allow-all-policy': minor
+'@backstage/plugin-scaffolder-backend-module-azure': minor
+'@backstage/plugin-scaffolder-backend-module-bitbucket-cloud': minor
+'@backstage/plugin-scaffolder-backend-module-bitbucket-server': minor
+'@backstage/plugin-scaffolder-backend-module-bitbucket': minor
+'@backstage/plugin-scaffolder-backend-module-confluence-to-markdown': minor
+'@backstage/plugin-scaffolder-backend-module-cookiecutter': minor
+'@backstage/plugin-scaffolder-backend-module-gcp': minor
+'@backstage/plugin-scaffolder-backend-module-gerrit': minor
+'@backstage/plugin-scaffolder-backend-module-gitea': minor
+'@backstage/plugin-scaffolder-backend-module-github': minor
+'@backstage/plugin-scaffolder-backend-module-gitlab': minor
+'@backstage/plugin-scaffolder-backend-module-notifications': minor
+'@backstage/plugin-scaffolder-backend-module-rails': minor
+'@backstage/plugin-scaffolder-backend-module-sentry': minor
+'@backstage/plugin-scaffolder-backend-module-yeoman': minor
+'@backstage/plugin-search-backend-module-stack-overflow-collator': minor
+'@backstage/plugin-signals-backend': minor
+---
+
+**BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs.
+
+This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config.
+
+As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it.
diff --git a/.changeset/swift-kings-sparkle.md b/.changeset/swift-kings-sparkle.md
deleted file mode 100644
index 84ceae962b..0000000000
--- a/.changeset/swift-kings-sparkle.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend-module-aws': patch
----
-
-`AwsOrganizationCloudAccountProcessor` configuration field `roleArn` is deprecated in favor of new field `accountId`
diff --git a/.changeset/swift-radios-enjoy.md b/.changeset/swift-radios-enjoy.md
new file mode 100644
index 0000000000..495117e3c6
--- /dev/null
+++ b/.changeset/swift-radios-enjoy.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-common': patch
+---
+
+Internal refactor to re-declare the token manager service which was removed from `@backstage/backend-plugin-api`, but is still supported in this package for backwards compatibility.
diff --git a/.changeset/tame-hornets-shake.md b/.changeset/tame-hornets-shake.md
new file mode 100644
index 0000000000..bf96193b55
--- /dev/null
+++ b/.changeset/tame-hornets-shake.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder-react': patch
+---
+
+Fix an issue where keys with duplicate final key parts are not all displayed in the `ReviewState`. Change the way the keys are formatted to include the full schema path, separated by `>`.
diff --git a/.changeset/tasty-pigs-vanish.md b/.changeset/tasty-pigs-vanish.md
new file mode 100644
index 0000000000..73556c70d5
--- /dev/null
+++ b/.changeset/tasty-pigs-vanish.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog': patch
+---
+
+Entity presentation api now only fetches fields that are required to display entity title
diff --git a/.changeset/thick-walls-share.md b/.changeset/thick-walls-share.md
new file mode 100644
index 0000000000..798b1561b4
--- /dev/null
+++ b/.changeset/thick-walls-share.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder-backend-module-gerrit': patch
+---
+
+Added test cases for publish:gerrit action examples
diff --git a/.changeset/twenty-clouds-melt.md b/.changeset/twenty-clouds-melt.md
new file mode 100644
index 0000000000..dee2b0f8ef
--- /dev/null
+++ b/.changeset/twenty-clouds-melt.md
@@ -0,0 +1,64 @@
+---
+'@backstage/backend-defaults': minor
+---
+
+**BREAKING**: The default backend instance no longer provides implementations for the identity and token manager services, which have been removed from `@backstage/backend-plugin-api`.
+
+If you rely on plugins that still require these services, you can add them to your own backend by re-creating the service reference and factory.
+
+The following can be used to implement the identity service:
+
+```ts
+import {
+ coreServices,
+ createServiceFactory,
+ createServiceRef,
+} from '@backstage/backend-plugin-api';
+import {
+ DefaultIdentityClient,
+ IdentityApi,
+} from '@backstage/plugin-auth-node';
+
+backend.add(
+ createServiceFactory({
+ service: createServiceRef({ id: 'core.identity' }),
+ deps: {
+ discovery: coreServices.discovery,
+ },
+ async factory({ discovery }) {
+ return DefaultIdentityClient.create({ discovery });
+ },
+ }),
+);
+```
+
+The following can be used to implement the token manager service:
+
+```ts
+import { ServerTokenManager, TokenManager } from '@backstage/backend-common';
+import { createBackend } from '@backstage/backend-defaults';
+import {
+ coreServices,
+ createServiceFactory,
+ createServiceRef,
+} from '@backstage/backend-plugin-api';
+
+backend.add(
+ createServiceFactory({
+ service: createServiceRef({ id: 'core.tokenManager' }),
+ deps: {
+ config: coreServices.rootConfig,
+ logger: coreServices.rootLogger,
+ },
+ createRootContext({ config, logger }) {
+ return ServerTokenManager.fromConfig(config, {
+ logger,
+ allowDisabledTokenManager: true,
+ });
+ },
+ async factory(_deps, tokenManager) {
+ return tokenManager;
+ },
+ }),
+);
+```
diff --git a/.changeset/twenty-queens-grow.md b/.changeset/twenty-queens-grow.md
new file mode 100644
index 0000000000..1236488dbe
--- /dev/null
+++ b/.changeset/twenty-queens-grow.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder': minor
+---
+
+Added EntityOwnerPicker component to the TemplateListPage to allow filtering on owner
diff --git a/.changeset/unlucky-cycles-clean.md b/.changeset/unlucky-cycles-clean.md
new file mode 100644
index 0000000000..5b582dec59
--- /dev/null
+++ b/.changeset/unlucky-cycles-clean.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder-react': patch
+---
+
+Scaffolder review page shows static amount of asterisks for secret fields.
diff --git a/.changeset/warm-boxes-grab-2.md b/.changeset/warm-boxes-grab-2.md
new file mode 100644
index 0000000000..f1928c8be7
--- /dev/null
+++ b/.changeset/warm-boxes-grab-2.md
@@ -0,0 +1,5 @@
+---
+'@backstage/frontend-plugin-api': patch
+---
+
+A new `apis` parameter has been added to `factory` for extensions. This is a way to access utility APIs without being coupled to the React context.
diff --git a/.changeset/warm-boxes-grab.md b/.changeset/warm-boxes-grab.md
new file mode 100644
index 0000000000..e10dbe086c
--- /dev/null
+++ b/.changeset/warm-boxes-grab.md
@@ -0,0 +1,5 @@
+---
+'@backstage/frontend-app-api': patch
+---
+
+Added the `root` extension the replace the `app` extension as the root of the app.
diff --git a/.changeset/witty-years-cry.md b/.changeset/witty-years-cry.md
new file mode 100644
index 0000000000..48c566730b
--- /dev/null
+++ b/.changeset/witty-years-cry.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-permission-backend': patch
+---
+
+Deprecated `createRouter` and its router options in favour of the new backend system.
diff --git a/.changeset/yellow-bees-hope.md b/.changeset/yellow-bees-hope.md
new file mode 100644
index 0000000000..1ea8690c20
--- /dev/null
+++ b/.changeset/yellow-bees-hope.md
@@ -0,0 +1,5 @@
+---
+'@backstage/frontend-app-api': minor
+---
+
+Removed deprecated `icons` property passing to `createApp` and `createSpecializedApp`. Use `IconBundleBlueprint.make` to create extensions instead and include them in the app.
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 9cd3bc0690..a0c83a42bd 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -9,10 +9,10 @@ yarn.lock @backstage/maintainers @backst
*/yarn.lock @backstage/maintainers @backstage-service
/.changeset/*.md
/beps/0001-notifications-system @backstage/maintainers @backstage/notifications-maintainers
-/docs/assets/search @backstage/discoverability-maintainers
-/docs/features/search @backstage/discoverability-maintainers
+/docs/assets/search @backstage/search-maintainers
+/docs/features/search @backstage/search-maintainers
/docs/features/techdocs @backstage/techdocs-maintainers
-/docs/plugins/integrating-search-into-plugins.md @backstage/discoverability-maintainers
+/docs/plugins/integrating-search-into-plugins.md @backstage/search-maintainers
/packages/cli/src/commands/onboard @backstage/sharks
/packages/backend-openapi-utils @backstage/maintainers @backstage/reviewers @backstage/openapi-tooling-maintainers
/packages/techdocs-cli @backstage/techdocs-maintainers
@@ -50,8 +50,8 @@ yarn.lock @backstage/maintainers @backst
/plugins/explore-react @backstage/maintainers @backstage/reviewers @backstage/sda-se-reviewers
/plugins/fossa @backstage/maintainers @backstage/reviewers @backstage/sda-se-reviewers
/plugins/git-release-manager @backstage/maintainers @backstage/reviewers @erikengervall
-/plugins/home @backstage/discoverability-maintainers
-/plugins/home-* @backstage/discoverability-maintainers
+/plugins/home @backstage/home-maintainers
+/plugins/home-* @backstage/home-maintainers
/plugins/kafka @backstage/maintainers @backstage/reviewers @andrewthauer
/plugins/kafka-backend @backstage/maintainers @backstage/reviewers @andrewthauer
/plugins/kubernetes @backstage/kubernetes-maintainers
@@ -70,13 +70,11 @@ yarn.lock @backstage/maintainers @backst
/plugins/rollbar-backend @backstage/maintainers @backstage/reviewers @andrewthauer
/plugins/scaffolder @backstage/maintainers @backstage/reviewers @backstage/scaffolder-maintainers
/plugins/scaffolder-* @backstage/maintainers @backstage/reviewers @backstage/scaffolder-maintainers
-/plugins/search @backstage/discoverability-maintainers
-/plugins/search-* @backstage/discoverability-maintainers
+/plugins/search @backstage/search-maintainers
+/plugins/search-* @backstage/search-maintainers
/plugins/signals @backstage/maintainers @backstage/notifications-maintainers
/plugins/signals-* @backstage/maintainers @backstage/notifications-maintainers
/plugins/sonarqube @backstage/maintainers @backstage/reviewers @backstage/sda-se-reviewers
-/plugins/stack-overflow @backstage/discoverability-maintainers
-/plugins/stack-overflow-backend @backstage/discoverability-maintainers
/plugins/techdocs @backstage/techdocs-maintainers
/plugins/techdocs-* @backstage/techdocs-maintainers
/plugins/user-settings-backend @backstage/maintainers @backstage/reviewers @backstage/sda-se-reviewers
diff --git a/.github/issue-labeler.yml b/.github/issue-labeler.yml
index 6d74ce8902..c6c6ef115f 100644
--- a/.github/issue-labeler.yml
+++ b/.github/issue-labeler.yml
@@ -1,6 +1,6 @@
area:techdocs:
- '/(techdocs|tech-docs|tech docs)/i'
-area:discoverability:
+area:search:
- '/search/i'
area:events:
- '/events/i'
diff --git a/.github/labeler.yml b/.github/labeler.yml
index a58da1d2f7..e6568091b8 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -4,7 +4,11 @@ area:catalog:
- plugins/catalog/**/*
- plugins/catalog-*/**/*
- packages/catalog-*/**/*
-area:discoverability: # search + home
+area:home:
+ - changed-files:
+ - any-glob-to-any-file:
+ - plugins/home/**/*
+area:search:
- changed-files:
- any-glob-to-any-file:
- plugins/search/**/*
diff --git a/.github/vale/config/vocabularies/Backstage/accept.txt b/.github/vale/config/vocabularies/Backstage/accept.txt
index 1092947245..320bb2e0ce 100644
--- a/.github/vale/config/vocabularies/Backstage/accept.txt
+++ b/.github/vale/config/vocabularies/Backstage/accept.txt
@@ -85,7 +85,9 @@ debounce
Debounce
debuggability
declaratively
+deduplicate
deduplicated
+deduplication
deliverables
denormalized
dependabot
@@ -241,8 +243,10 @@ Mkdocs
monorepo
Monorepo
monorepos
+morgan
msgraph
msw
+multiton
mutex
mutexes
mysql
diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml
index e334a29b82..1afb7a9391 100644
--- a/.github/workflows/api-breaking-changes.yml
+++ b/.github/workflows/api-breaking-changes.yml
@@ -27,7 +27,7 @@ jobs:
run: git fetch --depth 1 origin ${{ github.base_ref }}
- name: setup-node
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 18.x
registry-url: https://registry.npmjs.org/
@@ -46,7 +46,7 @@ jobs:
cat ${{ github.event_path }} > event.json
- name: Upload Artifacts
- uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4
+ uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4
with:
name: preview-spec
path: |
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 135076ba40..168d6205e4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,7 +35,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: use node.js ${{ matrix.node-version }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
@@ -71,7 +71,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: use node.js ${{ matrix.node-version }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
@@ -216,7 +216,7 @@ jobs:
run: git fetch origin ${{ github.event.pull_request.base.ref }}
- name: use node.js ${{ matrix.node-version }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml
index 8b4709337a..9f305f9a20 100644
--- a/.github/workflows/deploy_docker-image.yml
+++ b/.github/workflows/deploy_docker-image.yml
@@ -31,7 +31,7 @@ jobs:
ref: ${{ github.event.client_payload.version && env.RELEASE_VERSION || github.ref }}
- name: use node.js ${{ matrix.node-version }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml
index a0cdae5774..eafa4a8fcd 100644
--- a/.github/workflows/deploy_microsite.yml
+++ b/.github/workflows/deploy_microsite.yml
@@ -31,7 +31,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: use node.js 18.x
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 18.x
registry-url: https://registry.npmjs.org/ # Needed for auth
@@ -66,7 +66,7 @@ jobs:
run: ls microsite/build && ls microsite/build/storybook
- name: Deploy both microsite and storybook to gh-pages
- uses: JamesIves/github-pages-deploy-action@5c6e9e9f3672ce8fd37b9856193d2a537941e66c # v4.6.1
+ uses: JamesIves/github-pages-deploy-action@94f3c658273cf92fb48ef99e5fbc02bd2dc642b2 # v4.6.3
with:
branch: gh-pages
folder: microsite/build
diff --git a/.github/workflows/deploy_nightly.yml b/.github/workflows/deploy_nightly.yml
index db9489dbb8..5488b9a189 100644
--- a/.github/workflows/deploy_nightly.yml
+++ b/.github/workflows/deploy_nightly.yml
@@ -22,7 +22,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: use node.js 18.x
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 18.x
registry-url: https://registry.npmjs.org/ # Needed for auth
diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml
index 81e95fab97..7ae5637986 100644
--- a/.github/workflows/deploy_packages.yml
+++ b/.github/workflows/deploy_packages.yml
@@ -77,7 +77,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: use node.js ${{ matrix.node-version }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
@@ -161,7 +161,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: use node.js ${{ matrix.node-version }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
diff --git a/.github/workflows/pr-review-comment-trigger.yaml b/.github/workflows/pr-review-comment-trigger.yaml
index ccf02d5aee..cfe0e65580 100644
--- a/.github/workflows/pr-review-comment-trigger.yaml
+++ b/.github/workflows/pr-review-comment-trigger.yaml
@@ -30,7 +30,7 @@ jobs:
run: |
mkdir -p ./pr
echo $PR_NUMBER > ./pr/pr_number
- - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
+ - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
with:
name: pr_number-${{ github.event.pull_request.number }}
path: pr/
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index ecf18d71ad..26a2fdde11 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -58,7 +58,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: 'Upload artifact'
- uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
+ uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
with:
name: SARIF file
path: results.sarif
@@ -67,6 +67,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
- uses: github/codeql-action/upload-sarif@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10
+ uses: github/codeql-action/upload-sarif@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14
with:
sarif_file: results.sarif
diff --git a/.github/workflows/sync_code-formatting.yml b/.github/workflows/sync_code-formatting.yml
index 9886c94830..da6bd2592d 100644
--- a/.github/workflows/sync_code-formatting.yml
+++ b/.github/workflows/sync_code-formatting.yml
@@ -20,7 +20,7 @@ jobs:
fetch-depth: 0
- name: use node.js ${{ matrix.node-version }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
diff --git a/.github/workflows/sync_release-manifest.yml b/.github/workflows/sync_release-manifest.yml
index 6d02309f8a..62dd7e0c13 100644
--- a/.github/workflows/sync_release-manifest.yml
+++ b/.github/workflows/sync_release-manifest.yml
@@ -19,7 +19,7 @@ jobs:
ref: v${{ github.event.client_payload.version }}
- name: use node.js 18.x
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 18.x
registry-url: https://registry.npmjs.org/ # Needed for auth
diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml
index 1cdc0cb22a..af2e4da251 100644
--- a/.github/workflows/sync_snyk-github-issues.yml
+++ b/.github/workflows/sync_snyk-github-issues.yml
@@ -19,7 +19,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: use node.js 18.x
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 18.x
registry-url: https://registry.npmjs.org/ # Needed for auth
@@ -29,7 +29,7 @@ jobs:
cache-prefix: ${{ runner.os }}-v18.x
- name: Create Snyk report
- uses: snyk/actions/node@640e31719aac3e44867d239dc86c20c3e34c8e4f # master
+ uses: snyk/actions/node@6312a53377a551c0258438bf25fb8f378afbc977 # master
continue-on-error: true # Snyk CLI exits with error when vulnerabilities are found
with:
args: >
diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml
index b1e50397ce..1bdf0d30cf 100644
--- a/.github/workflows/sync_snyk-monitor.yml
+++ b/.github/workflows/sync_snyk-monitor.yml
@@ -31,7 +31,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Monitor and Synchronize Snyk Policies
- uses: snyk/actions/node@640e31719aac3e44867d239dc86c20c3e34c8e4f # master
+ uses: snyk/actions/node@6312a53377a551c0258438bf25fb8f378afbc977 # master
with:
command: monitor
args: >
@@ -46,7 +46,7 @@ jobs:
# Above we run the `monitor` command, this runs the `test` command which is
# the one that generates the SARIF report that we can upload to GitHub.
- name: Create Snyk report
- uses: snyk/actions/node@640e31719aac3e44867d239dc86c20c3e34c8e4f # master
+ uses: snyk/actions/node@6312a53377a551c0258438bf25fb8f378afbc977 # master
continue-on-error: true # To make sure that SARIF upload gets called
with:
args: >
@@ -58,6 +58,6 @@ jobs:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
NODE_OPTIONS: --max-old-space-size=7168
- name: Upload Snyk report
- uses: github/codeql-action/upload-sarif@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10
+ uses: github/codeql-action/upload-sarif@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14
with:
sarif_file: snyk.sarif
diff --git a/.github/workflows/verify_accessibility.yml b/.github/workflows/verify_accessibility.yml
index bb46bbd4c1..386306dcb1 100644
--- a/.github/workflows/verify_accessibility.yml
+++ b/.github/workflows/verify_accessibility.yml
@@ -26,7 +26,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Use Node.js 18.x
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 18.x
- name: yarn install
diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml
index e19187b6ce..b2fb2dc6ec 100644
--- a/.github/workflows/verify_codeql.yml
+++ b/.github/workflows/verify_codeql.yml
@@ -55,7 +55,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10
+ uses: github/codeql-action/init@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -66,7 +66,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10
+ uses: github/codeql-action/autobuild@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -80,4 +80,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10
+ uses: github/codeql-action/analyze@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14
diff --git a/.github/workflows/verify_e2e-kubernetes.yml b/.github/workflows/verify_e2e-kubernetes.yml
index b7465945ea..4647505cbf 100644
--- a/.github/workflows/verify_e2e-kubernetes.yml
+++ b/.github/workflows/verify_e2e-kubernetes.yml
@@ -29,7 +29,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: use node.js ${{ matrix.node-version }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml
index d6491f1e3c..fa303ce1c6 100644
--- a/.github/workflows/verify_e2e-linux.yml
+++ b/.github/workflows/verify_e2e-linux.yml
@@ -53,7 +53,7 @@ jobs:
git config --global user.name 'GitHub e2e user'
- name: use node.js ${{ matrix.node-version }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml
index 41f76a6410..80686763f4 100644
--- a/.github/workflows/verify_e2e-techdocs.yml
+++ b/.github/workflows/verify_e2e-techdocs.yml
@@ -35,7 +35,7 @@ jobs:
egress-policy: audit
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
+ - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1
with:
python-version: '3.9'
diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml
index d7575cadf7..ff579958bb 100644
--- a/.github/workflows/verify_e2e-windows.yml
+++ b/.github/workflows/verify_e2e-windows.yml
@@ -50,13 +50,13 @@ jobs:
git config --global user.name 'GitHub e2e user'
- name: use node.js ${{ matrix.node-version }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: setup python
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
+ uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1
with:
python-version: '3.10'
diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml
index 1a5bc8075b..ce8391a7eb 100644
--- a/.github/workflows/verify_microsite.yml
+++ b/.github/workflows/verify_microsite.yml
@@ -31,10 +31,10 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: use node.js 18.x
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 18.x
- - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5
+ - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5
with:
python-version: '3.9'
diff --git a/.github/workflows/verify_microsite_accessibility.yml b/.github/workflows/verify_microsite_accessibility.yml
index c9ed1696cd..220e2633c3 100644
--- a/.github/workflows/verify_microsite_accessibility.yml
+++ b/.github/workflows/verify_microsite_accessibility.yml
@@ -22,7 +22,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Use Node.js 18.x
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: 18.x
diff --git a/.github/workflows/verify_storybook.yml b/.github/workflows/verify_storybook.yml
index 83ddac8429..e9da9f0398 100644
--- a/.github/workflows/verify_storybook.yml
+++ b/.github/workflows/verify_storybook.yml
@@ -37,7 +37,7 @@ jobs:
fetch-depth: 0 # Required to retrieve git history
- name: use node.js ${{ matrix.node-version }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
@@ -51,7 +51,7 @@ jobs:
- run: yarn build-storybook
- - uses: chromaui/action@5991575112b472a4fa85429f29cc17e2fb83a2a2 # v11
+ - uses: chromaui/action@fdbe7756d4dbf493e2fbb822df73be7accd07e1c # v11
with:
token: ${{ secrets.GITHUB_TOKEN }}
# projectToken intentionally shared to allow collaborators to run Chromatic on forks
diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml
index 736193cad6..109e2ee96e 100644
--- a/.github/workflows/verify_windows.yml
+++ b/.github/workflows/verify_windows.yml
@@ -36,7 +36,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: use node.js ${{ matrix.node-version }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
+ uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
diff --git a/ADOPTERS.md b/ADOPTERS.md
index 870b4c93d4..76114b6965 100644
--- a/ADOPTERS.md
+++ b/ADOPTERS.md
@@ -134,7 +134,6 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/
| [SafetyCulture](https://safetyculture.com/) | [@R-cen](https://github.com/R-cen), [@lachlancooper](https://github.com/lachlancooper), [@hkf57](https://github.com/hkf57) | Internal developer portal to provide a centralized place for engineers to see an overview of their team's services and information related to the service from other systems. Initially focused on the software catalog, techdocs and search. |
| [Sana Life Science](https://sanalifescience.com) | [Joe Hillyard](mailto:joe@sanalifescience.com) | API Catalog, Tools Management & Control Hub |
| [Ndustrial](https://ndustrial.io) | [Jonathan Skubic](mailto:jonathan@ndustrial.io) | Software Project Catalog |
-| [TUI Musement](https://www.musement.com/uk/) | [Simone Fumagalli](mailto:simone.fumagalli@musement.com) | We are importing our catalog into it to keep it under control. The next step is start using templates |
| [Kambi AB](https://www.kambi.com) | [Martin Norum](mailto:martin.norum@kambi.com) | We want to kick ass at speed, so we're currently building up a catalog of our existing software, and looking into how Backstage can support us in our journey towards autonomous product teams. Both to improve speed to market and operational awareness. |
| [ANZ](https://www.anz.com.au/personal/) | [Elliot Jackson](mailto:elliot.jackson@anz.com) | Catalog, tech docs and automation |
| [Genie Solutions](https://www.geniesolutionssoftware.com.au) | [Zainab Bagasrawala](mailto:zainabbagasrawala@geniesolutions.com.au) | Developer Portal to track our projects, documentation, observability tools and more |
@@ -274,3 +273,4 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/
| [Grupo OLX](https://www.olx.com.br/) | [@marciorgb](https://github.com/marciorgb), [@jeffbraga](https://github.com/jeffbraga), [@rogerfernandes](https://github.com/rogerfernandes), [@stockrt](https://github.com/stockrt) | At Grupo OLX, we are using Backstage to reduce friction and enhance developer autonomy by creating Golden Paths, which automate previously manual processes. Additionally, Backstage's catalog integrates various views of our organizational structure and workflows, offering a comprehensive overview of how different components interconnect. |
| [DPG Media](https://www.dpgmediagroup.com) | [@benjidotsh](https://github.com/benjidotsh), [@lvstb](https://github.com/lvstb), [@ssliman](https://github.com/ssliman), [@edewel-beep](https://github.com/edewel-beep) | Access to infrastructure, documentation and internal tooling |
| [Baloise](https://www.baloise.ch) | [@dweber019](https://github.com/dweber019), [@mikevader](https://github.com/mikevader) | We are using Backstage as internal developer protal for use cases like software catalog, documentation, infrastructure. |
+| [Cigna](https://www.cigna.com/) / [Evernorth](https://www.evernorth.com/) | [@raphtalia](https://github.com/raphtalia), [Devon Paluso](mailto:Devon.Paluso@evernorth.com), [Bryan Rego](mailto:Bryan.Rego@evernorth.com), [Ian Skillings](mailto:Ian.Skillings@evernorth.com) | Internal developer portal providing TechDocs, software templates, centralized secret management, with information consolidation from ServiceNow, Okta, GitHub, GitLab, and WebEx. |
diff --git a/OWNERS.md b/OWNERS.md
index 5012181d21..b145205539 100644
--- a/OWNERS.md
+++ b/OWNERS.md
@@ -32,11 +32,11 @@ Scope: The catalog plugin and catalog model
| Patrik Oldsberg | Spotify | Cubic Belugas | [Rugvip](https://github.com/Rugvip) | `Rugvip#0019` |
| Vincenzo Scamporlino | Spotify | Cubic Belugas | [vinzscam](http://github.com/vinzscam) | `vinzscam#6944` |
-### Discoverability
+### Home
-Team: @backstage/discoverability-maintainers
+Team: @backstage/home-maintainers
-Scope: Discoverability within Backstage, including the home page, information architecture, and search
+Scope: The Backstage home page and information architecture
| Name | Organization | Team | GitHub | Discord |
| ------------------------ | ------------ | -------------- | ---------------------------------------- | ----------------- |
@@ -46,6 +46,18 @@ Scope: Discoverability within Backstage, including the home page, information ar
| Raghunandan Balachandran | Spotify | Infinite Buck$ | [soapraj](http://github.com/soapraj) | raghunandanb#1114 |
| Bailey Brooks | Spotify | Infinite Buck$ | [bailey](http://github.com/bailey) | \_bailey4 |
+### Search
+
+Team: @backstage/search-maintainers
+
+Scope: The Backstage Search plugin
+
+| Name | Organization | Team | GitHub | Discord |
+| ------------------------ | ------------ | -------------- | --------------------------------------------- | ----------------- |
+| Emma Indal | Spotify | Infinite Buck$ | [emmaindal](http://github.com/emmaindal) | emmaindal#7503 |
+| Raghunandan Balachandran | Spotify | Infinite Buck$ | [soapraj](http://github.com/soapraj) | raghunandanb#1114 |
+| Alex Lorenzi | Spotify | ProTean | [AlexLorenzi](https://github.com/AlexLorenzi) | alexlorenzi |
+
### Helm Charts
Team: @backstage/helm-chart-maintainers
diff --git a/README-zh_Hans.md b/README-zh_Hans.md
index 2813ee0793..129c7e7aa4 100644
--- a/README-zh_Hans.md
+++ b/README-zh_Hans.md
@@ -55,7 +55,7 @@ Backstage 的文档包括:
- [Discord 聊天室](https://discord.gg/backstage-687207715902193673) - 获得支持或讨论项目
- [参与贡献 Backstage](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) - 如果您想做出贡献,请从这里开始
- [RFCs](https://github.com/backstage/backstage/labels/rfc) - 帮助制定技术方向
-- [FAQ](https://backstage.io/docs/FAQ) - n.: 常问问题
+- [FAQ](https://backstage.io/docs/FAQ) - 常问问题
- [行为准则](CODE_OF_CONDUCT.md) - 这是我们的行事方式
- [采纳者](ADOPTERS.md) - 已经在使用 Backstage 的公司
- [博客](https://backstage.io/blog/) - 公告和更新
diff --git a/README.md b/README.md
index 6c27d01db2..4cba1082b0 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,5 @@
[](https://backstage.io/)
-> [!NOTE]
-> 🏖 From July 1st through 16th, due to maintainers being on summer vacations, expect the project to move a little slower than normal, and support to be limited. Normal service will resume after that! 🏝
-
# [Backstage](https://backstage.io)
English \| [한국어](README-ko_kr.md) \| [中文版](README-zh_Hans.md) \| [Français](README-fr_FR.md)
diff --git a/beps/0008-docs-personas-framework-portal/README.md b/beps/0008-docs-personas-framework-portal/README.md
index 07468b7c4a..1ef073a053 100644
--- a/beps/0008-docs-personas-framework-portal/README.md
+++ b/beps/0008-docs-personas-framework-portal/README.md
@@ -3,10 +3,11 @@ title: Improved Backstage Documentation with Personas
status: provisional
authors:
- '@waldirmontoya25'
- - '@aramissennyeyd'
+ - '@aramissennyeydd'
owners:
+ - '@backstage/documentation-maintainers'
project-areas:
- - core
+ - documentation
creation-date: 2024-03-18
---
@@ -23,22 +24,22 @@ creation-date: 2024-03-18
- [Design Details](#design-details)
- [Personas](#personas)
- [User](#user)
- - [Documentation Style](#documentation-style)
- - [Administrator](#administrator)
- - [Documentation Style](#documentation-style-1)
- - [Integrator](#integrator)
- - [Documentation Style](#documentation-style-2)
+ - [Documentation Content](#documentation-content)
+ - [Operator](#operator)
+ - [Documentation Content](#documentation-content-1)
+ - [Builder](#builder)
+ - [Documentation Content](#documentation-content-2)
- [Contributor](#contributor)
- - [Documentation Style](#documentation-style-3)
+ - [Documentation Content](#documentation-content-3)
- [Business Stakeholder](#business-stakeholder)
- - [Documentation Style](#documentation-style-4)
+ - [Documentation Content](#documentation-content-4)
- [Release Plan](#release-plan)
- [Dependencies](#dependencies)
- [Example Table of Contents](#example-table-of-contents)
## Summary
-This BEP proposes restructuring the Backstage documentation to emphasize the dual nature of Backstage as both a framework for building developer portals and a fully functional developer portal out of the box, as demonstrated by the demo site. The documentation will be divided into two main sections: One focusing on the developer portal that users get with the core plugins, and another on the framework that allows integrators and builders to create their own developer portal. The goal is to improve clarity, navigation, and adoption of Backstage by positioning it as both a ready-to-use developer portal and a framework for building custom developer portals.
+This BEP proposes restructuring the Backstage documentation to emphasize the dual nature of Backstage as both a framework for building developer portals and a fully functional developer portal out of the box, as demonstrated by the demo site. The documentation will be divided into two main sections: One focusing on the developer portal that users get with the core plugins, and another on the framework that allows operators and builders to create their own developer portal. The goal is to improve clarity, navigation, and adoption of Backstage by positioning it as both a ready-to-use developer portal and a framework for building custom developer portals.
## Motivation
@@ -48,8 +49,8 @@ The current Backstage documentation has been reported to be difficult to navigat
- Divide the documentation into two section: Framework and Developer Portal
- Define the personas Backstage is targeting
-- Structure the documentation to cater the different personas
-- Move the existing content to the appropriate section
+- Provide complete, clear and easy-to-find instructions for tasks required of personas
+- Move the existing content to the appropriate section.
### Non-Goals
@@ -63,14 +64,14 @@ The proposed restructuring of the Backstage documentation revolves around two co
1. Positioning Backstage as both a framework to build developer portals and a developer portal itself, and splitting the documentation into two main sections:
- Developer Portal: Focusing on the features, configuration, and usage of the developer portal that users get out of the box with the core plugins.
- - Framework: Covering the aspects of Backstage as a framework, including guides for integrators and builders who want to create their own developer portal using Backstage.
+ - Framework: Covering the aspects of Backstage as a framework, including guides for operators and builders who want to create their own developer portal using Backstage.
2. Defining the personas participating in Backstage adoption journeys to improve documentation navigation. The identified personas are:
- **End User**: A person who uses Backstage to find information, use plugins, and consume the developer portal.
- - **Administrator/Operator**: A person who configures, secures, and deploys the developer portal, manages plugins, and oversees the general administration of the developer portal.
- - **Integrator/Builder**: A person who builds plugins, customizes the code and design, and creates custom-built developer portals based on the Backstage framework. This includes developers and designers and anyone adding new functionality to their own Backstage instance.
- - **Product Manager/Business stakeholders**: A person who defines the strategy for adopting Backstage, identifies use cases, communicates the value proposition for adopting Backstage and connects the developer portal to the business strategy.
+ - **Operator**: A person who configures, secures, and deploys the developer portal, manages plugins, and oversees the general administration of the developer portal.
+ - **Builder**: A person who builds plugins, customizes the code and design, and creates custom-built developer portals based on the Backstage framework. This includes developers and designers and anyone adding new functionality to their own Backstage instance.
+ - **Business Stakeholder**: A person who defines the strategy for adopting Backstage, identifies use cases, communicates the value proposition for adopting Backstage and connects the developer portal to the business strategy.
- **Contributor**: A person who contributes to the Backstage upstream ecosystem.
The adoption strategy would be as follows:
@@ -90,6 +91,7 @@ The benefits of restructuring the documentation according to these ideas include
- The Docs section of the microsite will be divided into two top-level sections: Framework and Developer Portal.
- The structure of the Table of Contents will align with the outline proposed in https://github.com/backstage/backstage/issues/21946.
+- The developer portal documentation will be the primary entry point. Users should hit this site when navigating to backstage.io/docs. The framework documentation should be hosted similarly, but will be introduced organically in the developer portal documentation when referencing customization and other builder facing tasks.
### Personas
@@ -97,7 +99,7 @@ The benefits of restructuring the documentation according to these ideas include
Users navigate the developer portal to access tools, information, and plugins essential for their daily tasks. They rely on Backstage to effortlessly find resources, utilize integrations, and connect with other tools and services within their ecosystem. Their interaction is predominantly with the frontend of the portal, where ease of use, accessibility, and relevant content discovery are critical.
-##### Documentation Style
+##### Documentation Content
Documentation for this persona should be about usability of the portal once it is running. For example:
@@ -112,13 +114,13 @@ Documentation for this persona should be about usability of the portal once it i
- Using available plugins
- Step by step tutorials
-#### Administrator
+#### Operator
-Administrators are responsible for the behind-the-scenes technical setup and maintenance of the Backstage portal. This includes deploying the portal, configuring plugins, managing user access, and ensuring the security and performance of the system. They interact with both the frontend and backend, often using command-line tools, administrative dashboards, and configuration files to perform their tasks.
+Operators are responsible for the behind-the-scenes technical setup and maintenance of the Backstage portal. This includes deploying the portal, configuring plugins, managing user access, and ensuring the security and performance of the system. They interact with both the frontend and backend, often using command-line tools, administrative dashboards, and configuration files to perform their tasks.
-##### Documentation Style
+##### Documentation Content
-Documentation written for this persona should be DevOps technical, assuming a strong DevOps background. The goal with administrator documentation is to give administrators a strong understanding of how to deploy and manage a Backstage Developer Portal, best practices. For example:
+Documentation written for this persona should be DevOps technical, assuming a strong DevOps background. The goal is to give operators a strong understanding of how to deploy and manage a Backstage Developer Portal, best practices. For example:
- Installing and upgrading
- Configuring
@@ -126,17 +128,17 @@ Documentation written for this persona should be DevOps technical, assuming a st
- Plugins
- Ingesting data (users/groups/components, etc)
- Installing plugins
-- Implement Git Flows for the Developer portal
+- Implement Git Flows for the Developer Portal
- Creating Pipelines for Docs generation
- Troubleshooting
-#### Integrator
+#### Builder
-Integrators actively work on extending and customizing Backstage. This includes developing new plugins, customizing the UI/UX, and integrating external services or data sources. Their work is deeply technical, involving coding, and engaging with the Backstage community for support and collaboration. They need a deep understanding of the Backstage architecture and APIs, working closely with both the framework's backend and frontend aspects.
+Builders actively work on extending and customizing Backstage. This includes developing new plugins, customizing the UI/UX, and integrating external services or data sources. Their work is deeply technical, involving coding, and engaging with the Backstage community for support and collaboration. They need a deep understanding of the Backstage architecture and APIs, working closely with both the framework's backend and frontend aspects.
-##### Documentation Style
+##### Documentation Content
-Documentation written for this persona should be software technical, assuming a strong software background. While we can assume an overall technical knowledge, where possible we should link out to useful guides for the technologies we use, ex: Node.js, express.js, React, etc. The goal with documentation written for integrators is to give them a strong understanding of how to use the Backstage framework to build/evolve a company's Backstage Developer Portal, orient them to get support from the open source community, and prepare them for continuing to deliver value for their Backstage Developer Portal. For example:
+Documentation written for this persona should be software technical, assuming a strong software background. While we can assume an overall technical knowledge, where possible we should link out to useful guides for the technologies we use, ex: Node.js, express.js, React, etc. The goal with documentation written for this persona is to give them a strong understanding of how to use the Backstage framework to build/evolve a company's Backstage Developer Portal, orient them to get support from the open source community, and prepare them for continuing to deliver value for their Backstage Developer Portal. For example:
- API references
- Frontend and Backend systems
@@ -151,7 +153,9 @@ Documentation written for this persona should be software technical, assuming a
Contributors are involved in the development of the Backstage framework itself. They contribute to the core codebase, develop new features, fix bugs, create documentation and maintain the overall health of the project. They are deeply involved in the open-source community, collaborating with maintainers and other contributors to improve the framework and its ecosystem.
-##### Documentation Style
+Documentation for the contributor role should _not_ exist on the docs site. It should exist solely in the Github repositories (backstage/backstage, backstage/community-plugins). It can be linked to from the site, but should not have dedicated guides outside of the Github repositories.
+
+##### Documentation Content
The goal with documentation written for contributors is to give them a strong understanding of how to contribute to the Backstage framework, orient them to get support from the open source community, and prepare them for continuing to deliver value for the Backstage framework. For example:
@@ -164,7 +168,7 @@ The goal with documentation written for contributors is to give them a strong un
Business stakeholders use Backstage to align technical capabilities with business goals, monitoring how features and plugins support operational efficiency, developer satisfaction, and strategic objectives. They are involved in defining the strategy and measuring the impact of the developer portal on the organization. They need to navigate through dashboards, reports, and analytics within Backstage to gather insights and make informed decisions.
-##### Documentation Style
+##### Documentation Content
Documentation written for this persona should be strategic, assuming a strong background in business development and strategy. The goal for business documentation is to give a strong understanding of what Backstage Developer Portal can do for their company, how to deliver value quickly and continuously and guides for pitching or driving Backstage adoption. For example:
@@ -175,9 +179,10 @@ Documentation written for this persona should be strategic, assuming a strong ba
## Release Plan
-- Release the BEP by 03/24/2024.
-- Discuss the changes with the community and gather feedback by 04/24/2024.
-- Implement the changes by 04/30/2024.
+- [x] Release the BEP (04/21/2024).
+- [ ] Discuss the changes with the community and gather feedback.
+- [ ] Implement the table of contents changes.
+- [ ] Create issues to track reviewing and rewriting existing documentation into these new personas.
## Dependencies
@@ -185,20 +190,87 @@ None
## Example Table of Contents
-- Overview
- - "The overview should introduce users to the concept of Backstage, what an IDP is, how to deliver value, why you should care about DevEx, etc."
- - What is Backstage?
- - Roadmap
- - Vision
- - Release and Versioning Policy
- - Backstage Threat Model
- - Logo assets
- - Support and community
+- Developer Portal
+ - Overview
+ - What is Backstage?
+ - Roadmap
+ - Vision
+ - User personas and use cases
+ - How this documentation is organized
+ - Framework
+ - Developer portal
+ - Release and Versioning Policy
+ - Backstage Threat Model
+ - Logo assets
+ - Support and community
+ - Architecture Overview
+ - Getting Started
+ - Operator Guides
+ - Developer Portal
+ - "How do I deploy, monitor, configure and verify my Backstage Developer Portal?"
+ - Installing and Configuring
+ - Database
+ - Authentication
+ - Installing plugins
+ - Customize the design
+ - Securing
+ - Deploying in Production
+ - Integrating with other systems
+ - Managing
+ - Monitoring
+ - Troubleshooting
+ - Upgrading
+ - Keeping backstage up to date
+ - Customizing
+ - Core Plugins
+ - "How do I install and configure Backstage Developer Portal with plugins."
+ - Home Page
+ - Installing and Configuring
+ - Software Catalog
+ - Overview
+ - The life of an Entity
+ - Catalog Configuration
+ - System Model
+ - YAML file format
+ - Entity Reference
+ - Well Known annotations
+ - Well known relations
+ - Well known statuses
+ - Creating the catalog graph
+ - Software Templates
+ - Overview
+ - Configuring
+ - Adding a new template
+ - Writing a template
+ - Built in actions
+ - TechDocs
+ - Overview
+ - Getting Started
+ - Architecture
+ - Installing and configuring
+ - Using Cloud Storage for TechDocs generated files
+ - Configuring CI/CD to generate and publish TechDocs sites
+ - TechDocs CLI
+ - Troubleshooting
+ - Kubernetes
+ - Installing and Configuring
+ - Authentication
+ - Troubleshooting
+ - Search
+ - Business Stakeholder Guides
+ - "How do I present Backstage to leadership, what are the benefits, why should I care, etc."
+ - Strategies for adopting
+ - Use cases
+ - User Guides
+ - "How do I use the default OSS Backstage"
+ - Logging in
+ - Registering a component
+ - Creating a new component
+ - Reference
- Framework
- Architecture Overview
- - "The arch overview should explain how the framework is structured, where plugins and instances fit in and how to understand the current design of Backstage."
- Getting Started
- - Integrator/Builder Guides
+ - Builder Guides
- Local Development
- "Prepare users for how to develop locally, debug problems, run tests, etc."
- CLI
@@ -280,68 +352,3 @@ None
- "How to get started contributing to OSS."
- Contributing to Backstage
- Reference
-- Developer Portal
- - Architecture Overview
- - Getting Started
- - Administrator Guides
- - Developer Portal
- - "How do I deploy, monitor, configure and verify my Backstage Developer Portal?"
- - Installing and Configuring
- - Database
- - Authentication
- - Installing plugins
- - Customize the design
- - Securing
- - Deploying in Production
- - Integrating with other systems
- - Managing
- - Monitoring
- - Troubleshooting
- - Upgrading
- - Keeping backstage up to date
- - Customizing
- - Core Plugins
- - "How do I install and configure Backstage Developer Portal with plugins."
- - Home Page
- - Installing and Configuring
- - Software Catalog
- - Overview
- - The life of an Entity
- - Catalog Configuration
- - System Model
- - YAML file format
- - Entity Reference
- - Well Known annotations
- - Well known relations
- - Well known statuses
- - Creating the catalog graph
- - Software Templates
- - Overview
- - Configuring
- - Adding a new template
- - Writing a template
- - Built in actions
- - TechDocs
- - Overview
- - Getting Started
- - Architecture
- - Installing and configuring
- - Using Cloud Storage for TechDocs generated files
- - Configuring CI/CD to generate and publish TechDocs sites
- - TechDocs CLI
- - Troubleshooting
- - Kubernetes
- - Installing and Configuring
- - Authentication
- - Troubleshooting
- - Search
- - Product Manager Guides
- - "How do I present Backstage to leadership, what are the benefits, why should I care, etc."
- - Strategies for adopting
- - Use cases
- - User Guides
- - "How do I use the default OSS Backstage"
- - Logging in
- - Registering a component
- - Creating a new component
- - Reference
diff --git a/beps/0010-scaffolder-templating-in-parameters/README.md b/beps/0010-scaffolder-templating-in-parameters/README.md
new file mode 100644
index 0000000000..354dc66ea9
--- /dev/null
+++ b/beps/0010-scaffolder-templating-in-parameters/README.md
@@ -0,0 +1,223 @@
+---
+title: Supporting templating syntax in `parameters` schema
+status: provisional
+authors:
+ - '@benjdlambert'
+owners:
+ - '@benjdlambert'
+ - '@backstage/scaffolder-maintainers'
+project-areas:
+ - scaffolder
+creation-date: 2024-03-26
+---
+
+
+
+# BEP: Supporting templating syntax in `parameters` schema
+
+
+
+[**Discussion Issue**](https://github.com/backstage/backstage/issues/16275)
+
+- [Summary](#summary)
+- [Motivation](#motivation)
+ - [Goals](#goals)
+ - [Non-Goals](#non-goals)
+- [Proposal](#proposal)
+- [Design Details](#design-details)
+- [Release Plan](#release-plan)
+- [Dependencies](#dependencies)
+- [Alternatives](#alternatives)
+
+## Summary
+
+
+
+This BEP proposes to add support for templating syntax in the `parameters` schema of a scaffolder template.
+This will allow users to define properties in the JSON Schema which are templated from current values that have been collected from the user already.
+This can be useful when you want to use a value that has already been collected as a default value in another field.
+
+For example:
+
+```yaml
+apiVersion: scaffolder.backstage.io/v1beta3
+kind: Template
+metadata:
+ name: my-template
+spec:
+ parameters:
+ - title: Some input
+ description: Get some info from the user
+ properties:
+ name:
+ type: string
+ default: Test
+ description:
+ type: string
+ default: ${{ parameters.name or "unknown" }}-description
+```
+
+## Motivation
+
+
+
+Inclusive of the initial RFC there's been a swarm of issues that are requesting this feature, and we want to align on the implementation and design of this feature.
+
+See the following:
+
+- https://github.com/backstage/backstage/issues/16275
+- https://github.com/backstage/backstage/pull/23283
+- https://github.com/backstage/backstage/issues/19597
+- https://github.com/backstage/backstage/issues/20533
+- https://github.com/backstage/backstage/pull/17746
+
+There's some ideas for introducing a templating syntax for both templating into the `parameters` schema, and also being able to pass through some templating strings to underlying field extensions that can use those templating strings.
+We want to align here so that we're not going to have those conflict or compete, and create a standard for how to achieve templating in both circumstances.
+
+### Goals
+
+
+
+- This BEP will settle the implementation for the templating of fields into the JSON Schema in the `parameters` section in the scaffolder templates.
+- This BEP will settle how to pass through templating strings to underlying field extensions in a non-conflicting way.
+
+### Non-Goals
+
+
+
+## Proposal
+
+The proposal is to be able to decorate the template schema server side with a context and use that to drive the form rendering client side.
+
+We can extend the `/parameter-schema` endpoint to accept a `formData` context query parameter which will be a JSON object of the current `formData` state. This in turn allows the scaffolder frontend to repeatedly call the endpoint to get the updated rendered parameter schema. We'll need to turn the endpoint into a `POST` endpoint to accept the form data, but will retain the `GET` version for backwards compatibility.
+
+## Design Details
+
+### Example implementation of the `/parameter-schema` endpoint
+
+```diff
+export interface ScaffolderApi {
+ getTemplateParameterSchema(
+ templateRef: string,
++ formData?: JsonObject,
+ ): Promise;
+}
+```
+
+```diff
+ router
+- .get(
++ .post(
+ '/v2/templates/:namespace/:kind/:name/parameter-schema',
+ async (req, res) => {
+ const credentials = await httpAuth.credentials(req);
+ const { token } = await auth.getPluginRequestToken({
+ onBehalfOf: credentials,
+ targetPluginId: 'catalog',
+ });
+ const template = await authorizeTemplate(
+ req.params,
+ token,
+ credentials,
+ );
+
+ const parameters = [template.spec.parameters ?? []].flat();
++ const secureTemplater = await SecureTemplater.loadRenderer({
++ templateFilters: {
++ ...createDefaultFilters({ integrations }),
++ ...additionalTemplateFilters,
++ },
++ templateGlobals: additionalTemplateGlobals,
++ });
++
++ const templatedParameters = parameters.map(parameter =>
++ renderTemplateString(
++ parameter,
++ {
++ parameters: req.body.formData,
++ },
++ secureTemplater,
++ logger,
++ ),
++ );
+```
+
+You can see a quick implementation of this in this [branch](https://github.com/backstage/backstage/compare/master...blam/templating-in-parameters)
+
+### Workaround for the `default` field
+
+There's a slight issue with the implementation of the `react-jsonschema-form`, which makes things like live updating on things like the `default` field slightly more difficult.
+Currently, on first render, the default value is populated and then stored in the `formData` object or the current state, and the default value is never re-evaluated again at a later stage.
+
+This means that if end users are wanting to set default values with `${{ parameters.myOtherProperty }}`, then they would need to ensure that they are on different steps in the form
+as the form would need to be re-rendered, and for performance reasons, we don't want to re-render the form on every `formData` update.
+
+We could fix this, by implementing custom logic for when the `parameter-schema` is updated, if the updated field is in a `default: *` field, then we replace the previous value with the new value in the `formData` automatically.
+This is a pretty ugly workaround, but maybe the only option we have. Also at this point, pretty unsure if this affects any other parts of the `JSONSchema`, and we would also have to implement it for those fields if they exist.
+
+### Templated error messages
+
+Templating for `errorMessages` has been solved by using the `ajv-errors` library https://github.com/backstage/backstage/pull/25624, you can see more about [`backrefs` and pointers here](https://ajv.js.org/packages/ajv-errors.html). Any other template strings that will be passed through the underlying components and to be left untemplated should be encapsulated with options instead of passing through raw strings. The below example illustrates an `entityAndName` format, which under the hood, might do something like `${{ parameters.entity }} - ${{ parameters.name }}`, but this implementation never leaks out to the templating language.
+
+```yaml
+parameters:
+ properties:
+ ...
+ description:
+ type: string
+ default: Test-description
+ ui:field: CustomDisplayField
+ ui:options:
+ format: entityAndName
+```
+
+## Release Plan
+
+
+
+This change is backwards compatible, and can be released in a minor release. There's no breaking changes to worry about here.
+
+## Dependencies
+
+
+
+## Alternatives
+
+
+
+### Templating client side
+
+- This could lead to confusion as `filters` such as `parseRepoUrl` and `pick` and any custom filters which you define in the backend would not be available in the client side.
+
+- Also with the limitations of the `default` value being updated only on first render and never re-evaluated, there's no performance benefit of doing things client side anymore.
+
+### Accept limitation of the `default` field
+
+Rather than using a workaround to support re-evaluating the `default` field, we could instead accept it as a limitation, and document it as such.
+
+This is not desirable, as it is likely a very common use-case to want to template the `default` field, leading to a poor template creation experience.
diff --git a/beps/0011-event-auditor/README.md b/beps/0011-event-auditor/README.md
new file mode 100644
index 0000000000..44006599eb
--- /dev/null
+++ b/beps/0011-event-auditor/README.md
@@ -0,0 +1,187 @@
+---
+title: Event Auditor
+status: provisional
+authors:
+ - '@schultzp2020'
+owners:
+project-areas:
+ - core
+creation-date: 2024-06-04
+---
+
+# BEP: Event Auditor
+
+[**Discussion Issue**](https://github.com/backstage/backstage/issues/23950)
+
+- [BEP: Event Auditor](#bep-event-auditor)
+ - [Summary](#summary)
+ - [Motivation](#motivation)
+ - [Goals](#goals)
+ - [Non-Goals](#non-goals)
+ - [Proposal](#proposal)
+ - [Design Details](#design-details)
+ - [Data Model for Audit Events](#data-model-for-audit-events)
+ - [Actor Details Interface](#actor-details-interface)
+ - [Audit Request/Response Interfaces](#audit-requestresponse-interfaces)
+ - [Audit Event Status Interfaces](#audit-event-status-interfaces)
+ - [EventAuditor Interface](#eventauditor-interface)
+ - [Release Plan](#release-plan)
+ - [Dependencies](#dependencies)
+ - [Alternatives](#alternatives)
+
+## Summary
+
+This feature introduces a dedicated system for recording critical security-related actions and events within Backstage. By maintaining a distinct audit event stream, organizations benefit from enhanced security, improved regulatory compliance, efficient incident response, and ensured data integrity.
+
+## Motivation
+
+- Strengthen security by tracking user authentication, authorization, data access, and configuration changes.
+- Facilitate adherence to regulatory requirements through logging security-sensitive operations.
+- Enable efficient forensic analysis and investigation of security incidents.
+- Uphold the integrity of critical audit data by implementing robust access controls and tamper-proof measures.
+
+### Goals
+
+- Develop a backend service for the audit event stream to record security-critical events.
+- Ensure compliance with regulatory requirements through detailed audit event logging.
+ - Refer to [NIST: Audit and Accountability](https://csrc.nist.gov/projects/cprt/catalog#/cprt/framework/version/SP_800_53_5_1_1/home?element=AU)
+ - Refer to [NIST: Non-Repudiation](https://csrc.nist.gov/projects/cprt/catalog#/cprt/framework/version/SP_800_53_5_1_1/home?element=AU-10)
+- Establish a standardized data format for audit events.
+ - Refer to [NIST: Content of Audit Records](https://csrc.nist.gov/projects/cprt/catalog#/cprt/framework/version/SP_800_53_5_1_1/home?element=AU-03)
+- Provide access to the transport layer for customizable output options.
+
+### Non-Goals
+
+- Implementing mechanisms for event storage, analysis, or visualization.
+- Addressing security aspects of event storage and access control beyond initial separation from regular events.
+
+Both of these non-goals can be implemented as separate plugins.
+
+## Proposal
+
+The proposal introduces a crucial change to implement a dedicated audit event stream in Backstage. We recommend creating a new backend service using Winston to establish a distinct channel specifically for audit events. This service would act as a wrapper around Winston, providing methods with strict interfaces to ensure uniformity across audit events throughout the Backstage application. By separating the configuration, we can clearly distinguish between regular application events and critical security events. The event format would include mandatory fields relevant to regulatory compliance and security investigations, such as actor, IP address, timestamp, and event details. This standardized format would streamline the analysis and investigation of security-related events.
+
+Overall, this approach offers several benefits. Separating the configuration allows for the clear distinction of security-critical events, enhancing monitoring and analysis. The standardized data format within the service ensures consistency and facilitates compliance with regulations. Finally, the service methods simplify the process of logging audit events.
+
+## Design Details
+
+### Data Model for Audit Events
+
+To ensure consistency and facilitate regulatory compliance, the proposal suggests creating a shared package that defines a data model for audit events. This model consists of several key components.
+
+#### Actor Details Interface
+
+This interface defines the information related to the actor who triggered the logged event. It includes fields like actor ID, IP address, hostname, and user agent.
+
+```ts
+export type ActorDetails = {
+ actorId?: string;
+ ip?: string;
+ hostname?: string;
+ userAgent?: string;
+};
+```
+
+#### Audit Request/Response Interfaces
+
+These interfaces define the structure of request and response data that might be included in the audit event. It's important to note that these interfaces exclude sensitive information like tokens from headers or other irrelevant details to avoid security risks.
+
+```ts
+export type AuditRequest = {
+ url: string;
+ method: string;
+};
+
+export type AuditResponse = {
+ status: number;
+};
+```
+
+#### Audit Event Status Interfaces
+
+These interfaces define the possible statuses for an audit event entry. There are three options:
+
+```ts
+/**
+ * Indicates the event was successful.
+ */
+export type AuditEventSuccessStatus = { status: 'succeeded' };
+
+/**
+ * Indicates the event failed and includes details about the encountered errors.
+ */
+export type AuditEventFailureStatus = {
+ status: 'failed';
+ errors: E[];
+};
+
+export type AuditEventStatus =
+ | AuditEventSuccessStatus
+ | AuditEventFailureStatus
+ | undefined;
+```
+
+#### EventAuditor Interface
+
+This interface defines the functionalities of an `EventAuditor` class. This class provides methods for:
+
+- Extracting the actor ID from an Express request (if available).
+- Creating detailed audit event information based on provided options.
+- Logging an audit event with a specific level (info, debug, warn, or error).
+
+```ts
+/**
+ * Common fields of an audit event.
+ *
+ * @public
+ */
+export type AuditEventOptions = AuditEventStatus & {
+ eventName: string;
+ message: string;
+ stage: string;
+ level?: 'info' | 'debug' | 'warn' | 'error';
+ metadata?: JsonValue;
+ response?: AuditResponse;
+ request?: Request;
+} & ({ actorId: string } | { credentials: BackstageCredentials } | undefined);
+
+export type AuditEvent = {
+ actor: ActorDetails;
+ eventName: string;
+ stage: string;
+ isAuditLog: true;
+ request?: AuditRequest;
+ response?: AuditResponse;
+} & AuditLogStatus;
+
+export interface EventAuditor {
+ /**
+ * Processes an express request and obtains the actorId from it. Returns undefined if actorId is not obtainable.
+ *
+ * @public
+ */
+ getActorId(request?: Request): Promise;
+
+ /**
+ * Generates an Audit Event and logs it at the level passed by the user.
+ * Supports `info`, `debug`, `warn` or `error` level. Defaults to `info` if no level is passed.
+ *
+ * Secrets in the metadata field and request body, params and query field should be redacted by the user before passing in the request object
+ * @public
+ */
+ auditEvent(options: AuditEventOptions): Promise;
+}
+```
+
+## Release Plan
+
+The release plan involves initially creating a shared audit event package. Following this, the audit event will be implemented in core packages and other plugins. The first targets should be high-priority areas, such as the scaffolder and catalog systems. Since adding the audit event will not disrupt existing functionality, the release plan is simplified.
+
+## Dependencies
+
+- `@backstage/types`
+-
+
+## Alternatives
+
+N/A
diff --git a/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild b/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild
index 053b4fb492..fa8e42482b 100644
--- a/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild
+++ b/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild
@@ -55,4 +55,4 @@ COPY docker/default.conf.template /etc/nginx/templates/default.conf.template
COPY docker/inject-config.sh /docker-entrypoint.d/40-inject-config.sh
-ENV PORT 80
+ENV PORT=80
diff --git a/contrib/docker/frontend-with-nginx/Dockerfile.hostbuild b/contrib/docker/frontend-with-nginx/Dockerfile.hostbuild
index f0fa4a034b..b07d02c0c8 100644
--- a/contrib/docker/frontend-with-nginx/Dockerfile.hostbuild
+++ b/contrib/docker/frontend-with-nginx/Dockerfile.hostbuild
@@ -36,4 +36,4 @@ COPY docker/default.conf.template /etc/nginx/templates/default.conf.template
COPY docker/inject-config.sh /docker-entrypoint.d/40-inject-config.sh
-ENV PORT 80
+ENV PORT=80
diff --git a/contrib/docker/minimal-hardened-image/Dockerfile b/contrib/docker/minimal-hardened-image/Dockerfile
index 8ee6a3d206..b9435aa8af 100644
--- a/contrib/docker/minimal-hardened-image/Dockerfile
+++ b/contrib/docker/minimal-hardened-image/Dockerfile
@@ -9,53 +9,79 @@
#
# Once the commands have been run, you can build the image using `yarn docker-build`
-FROM cgr.dev/chainguard/wolfi-base:latest
+# syntax = docker/dockerfile:1.4
-ENV NODE_VERSION 18=~18.19
-ENV PYTHON_VERSION 3.12=~3.12
+# Build Python environment in a separate builder stage
+FROM cgr.dev/chainguard/python:latest-dev as python-builder
-RUN apk add nodejs-$NODE_VERSION yarn
+ENV PATH=/venv/bin:$PATH
-# 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.
-# Additionally, we install dependencies for `techdocs.generator.runIn: local`.
-# https://backstage.io/docs/features/techdocs/getting-started#disabling-docker-in-docker-situation-optional
-RUN --mount=type=cache,target=/var/cache/apk,sharing=locked \
- --mount=type=cache,target=/var/lib/apk,sharing=locked \
+RUN --mount=type=cache,target=/home/nonroot/.cache/pip,uid=65532,gid=65532 \
+ python3 -m venv /home/nonroot/venv && \
+ /home/nonroot/venv/bin/pip install mkdocs-techdocs-core==1.3.3
+
+# Build Node environment in a separate builder stage
+FROM cgr.dev/chainguard/wolfi-base:latest as node-builder
+
+ENV NODE_VERSION="20=~20.11"
+ENV NODE_ENV=production
+
+RUN --mount=type=cache,target=/var/cache/apk,sharing=locked,uid=65532,gid=65532 \
+ --mount=type=cache,target=/var/lib/apk,sharing=locked,uid=65532,gid=65532 \
apk update && \
- apk add sqlite-dev python-$PYTHON_VERSION py3-pip python-3-dev py3-setuptools build-base gcc libffi-dev glibc-dev openssl-dev brotli-dev c-ares-dev nghttp2-dev icu-dev zlib-dev gcc-12 libuv-dev && \
- yarn config set python /usr/bin/python3
+ apk add nodejs-$NODE_VERSION yarn \
+ # Install isolate-vm dependencies, these are needed by the @backstage/plugin-scaffolder-backend.
+ openssl-dev brotli-dev c-ares-dev nghttp2-dev icu-dev zlib-dev gcc-12 libuv-dev build-base
-# Set up a virtual environment for mkdocs-techdocs-core.
-ENV VIRTUAL_ENV=/opt/venv
-RUN python3 -m venv $VIRTUAL_ENV
-ENV PATH="$VIRTUAL_ENV/bin:$PATH"
-
-RUN pip3 install mkdocs-techdocs-core==1.3.3
-
-# From here on we use the least-privileged `node` user to run the backend.
WORKDIR /app
-RUN chown nonroot:nonroot /app
+RUN chown -R nonroot:nonroot /app
+
+RUN mkdir -p /home/nonroot/.yarn/berry && chown -R 65532:65532 /home/nonroot/.yarn/berry
+
USER nonroot
-# This switches many Node.js dependencies to production mode.
-ENV NODE_ENV production
+COPY --chown=65532:65532 .yarn ./.yarn
+COPY --chown=65532:65532 .yarnrc.yml ./
-# Copy over Yarn 3 configuration, release, and plugins
-COPY --chown=nonroot:nonroot .yarn ./.yarn
-COPY --chown=nonroot:nonroot .yarnrc.yml ./
-
-# Copy repo skeleton first, to avoid unnecessary docker cache invalidation.
-# The skeleton contains the package.json of each package in the monorepo,
-# and along with yarn.lock and the root package.json, that's enough to run yarn install.
-COPY --chown=nonroot:nonroot yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./
+COPY --chown=65532:65532 yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./
RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz
-RUN --mount=type=cache,target=/home/node/.yarn/berry/cache,sharing=locked,uid=1000,gid=1000 \
- yarn workspaces focus --all --production
+RUN --mount=type=cache,target=/home/nonroot/.yarn/berry/cache,sharing=locked,uid=65532,gid=65532 \
+ yarn workspaces focus --all --production && yarn cache clean --all
-# Then copy the rest of the backend bundle, along with any other files we might want.
-COPY --chown=nonroot:nonroot packages/backend/dist/bundle.tar.gz app-config*.yaml ./
-RUN tar xzf bundle.tar.gz && rm bundle.tar.gz
+# Final stage to build the application image
+FROM cgr.dev/chainguard/wolfi-base:latest
+ENV PYTHON_VERSION="3.12=~3.12"
+ENV NODE_VERSION="20=~20.14"
+ENV NODE_ENV=production
+
+RUN --mount=type=cache,target=/var/cache/apk,sharing=locked,uid=65532,gid=65532 \
+ --mount=type=cache,target=/var/lib/apk,sharing=locked,uid=65532,gid=65532 \
+ apk update && \
+ apk add \
+ # add node for backstage
+ nodejs-$NODE_VERSION \
+ # add python for backstage techdocs
+ python-$PYTHON_VERSION \
+ # add tini for init process
+ tini
+
+WORKDIR /app
+
+COPY package.json app-config.yaml ./
+ADD packages/backend/dist/skeleton.tar.gz packages/backend/dist/bundle.tar.gz ./
+
+RUN chown -R 65532:65532 /app
+RUN chown -R 65532:65532 /tmp
+USER 65532:65532
+
+COPY --from=node-builder --chown=65532:65532 /app/node_modules ./node_modules
+COPY --from=python-builder --chown=65532:65532 /home/nonroot/venv /home/nonroot/venv
+ENV PATH=/home/nonroot/venv/bin:$PATH
+
+ENV NODE_OPTIONS="--no-node-snapshot"
+ENV GIT_PYTHON_REFRESH="quiet"
+
+ENTRYPOINT ["tini", "--"]
CMD ["node", "packages/backend", "--config", "app-config.yaml"]
diff --git a/contrib/docker/minimal-hardened-image/README.md b/contrib/docker/minimal-hardened-image/README.md
index 62045a4ded..191c77986c 100644
--- a/contrib/docker/minimal-hardened-image/README.md
+++ b/contrib/docker/minimal-hardened-image/README.md
@@ -4,6 +4,19 @@ DockerHub images in general did not seem ideal for Backstage as the number of vu
The `Dockerfile` in this directory uses a [wolfi-base](https://github.com/wolfi-dev) image from Chainguard Images. This improves the security of the application and reduces false positives in scanners.
+## Steps taken
+
+When converting, I utilized the upstream Dockerfile as a starting point.
+
+- Multi-stage build - The Dockerfile has been split up into a multistage build which reduces the files, packages, executables, and directories in the final image.
+ - Size savings = ~900mb
+ - Reduced attack surface
+- Base Image - Swap to [wolfi-base](https://github.com/wolfi-dev) image from Chainguard Images
+ - Vulnerability Savings = ~239 at the time of updating this README
+- Entrypoint - Swap from `node` to `tini` as entrypoint to ensure that the default signal handlers work and zombie processes are handled properly
+- Use `ADD` instead of `COPY` in dockerfile to reduce copied compressed files
+ - When a `rm` is used to remove a compressed file it still makes its way into the final image. Using `ADD` is safe with local files.
+
## Pinning Digest
To reduce maintenance, the digest of the image has been removed from the `./Dockerfile` file. A complete example with the digest would be `cgr.dev/chainguard/wolfi-base:latest@sha256:3d6dece13cdb5546cd03b20e14f9af354bc1a56ab5a7b47dca3e6c1557211fcf` and it is suggested to update the `FROM` line in the `Dockerfile` to use a digest.
diff --git a/contrib/docs/tutorials/prometheus-metrics.md b/contrib/docs/tutorials/prometheus-metrics.md
index fb4366c7f7..0db97c649e 100644
--- a/contrib/docs/tutorials/prometheus-metrics.md
+++ b/contrib/docs/tutorials/prometheus-metrics.md
@@ -1,5 +1,8 @@
# Prometheus
+> [!NOTE]
+> The Prometheus metrics have been marked as deprecated and will be removed at a later point. The recommendation is to use the OpenTelemetry metrics by following the [Setup OpenTelemetry](https://backstage.io/docs/tutorials/setup-opentelemetry) documentation
+
## Overview
This is a small tutorial that goes over how to setup your Backstage instance to output metrics in a format that can be pulled in by Prometheus.
diff --git a/docs/assets/auth/github-provider-not-configured-to-support-sign-in.png b/docs/assets/auth/github-provider-not-configured-to-support-sign-in.png
new file mode 100644
index 0000000000..1bbd94ecc3
Binary files /dev/null and b/docs/assets/auth/github-provider-not-configured-to-support-sign-in.png differ
diff --git a/docs/assets/auth/github-unable-to-reolve-identity.png b/docs/assets/auth/github-unable-to-reolve-identity.png
new file mode 100644
index 0000000000..622fd75a4a
Binary files /dev/null and b/docs/assets/auth/github-unable-to-reolve-identity.png differ
diff --git a/docs/assets/getting-started/login-screen.png b/docs/assets/getting-started/login-screen.png
new file mode 100755
index 0000000000..1af52cb2c9
Binary files /dev/null and b/docs/assets/getting-started/login-screen.png differ
diff --git a/docs/auth/aws-alb/provider.md b/docs/auth/aws-alb/provider.md
index 8f21b7dbf0..a3974c895e 100644
--- a/docs/auth/aws-alb/provider.md
+++ b/docs/auth/aws-alb/provider.md
@@ -11,14 +11,18 @@ and get the user seamlessly authenticated.
## Configuration
The provider configuration can be added to your `app-config.yaml` under the root
-`auth` configuration:
+`auth` configuration, similar to the following example:
```yaml title="app-config.yaml"
auth:
providers:
awsalb:
- issuer: 'https://example.okta.com/oauth2/default' # optional
- region: 'us-west-2' # required, use your actual region here
+ # this is the URL of the IdP you configured
+ issuer: 'https://example.okta.com/oauth2/default'
+ # this is the ARN of your ALB instance
+ signer: 'arn:aws:elasticloadbalancing:us-east-2:123456789012:loadbalancer/app/my-load-balancer/1234567890123456'
+ # this is the region where your ALB instance resides
+ region: 'us-west-2'
signIn:
resolvers:
# typically you would pick one of these
@@ -26,6 +30,8 @@ auth:
- resolver: emailLocalPartMatchingUserEntityName
```
+Ensure that you have set the signer correctly. It is also recommended that you restrict your target groups' security policy to only accept connections from that ALB.
+
### Resolvers
This provider includes several resolvers out of the box that you can use:
diff --git a/docs/auth/cloudflare/provider.md b/docs/auth/cloudflare/provider.md
index 516ea9d647..a1946580a7 100644
--- a/docs/auth/cloudflare/provider.md
+++ b/docs/auth/cloudflare/provider.md
@@ -32,6 +32,12 @@ auth:
serviceTokens:
- token: '1uh2fh19efvfh129f1f919u21f2f19jf2.access'
subject: 'bot-user@your-company.com'
+ # You can customize the header name that contains the jwt token, by default
+ # cf-access-jwt-assertion is used
+ jwtHeaderName:
+ # You can customize the authorization cookie name, by default
+ # CF_Authorization is used
+ authorizationCookieName:
# This picks what sign in resolver(s) you want to use.
signIn:
resolvers:
diff --git a/docs/auth/google/gcp-iap-auth.md b/docs/auth/google/gcp-iap-auth.md
index 6479ac7268..298f212399 100644
--- a/docs/auth/google/gcp-iap-auth.md
+++ b/docs/auth/google/gcp-iap-auth.md
@@ -24,7 +24,7 @@ Let's start by adding the following `auth` configuration in your
```yaml
auth:
providers:
- gcp-iap:
+ gcpIap:
audience: '/projects//global/backendServices/'
jwtHeader: x-custom-header # Optional: Only if you are using a custom header for the IAP JWT
signIn:
@@ -77,6 +77,6 @@ backend.add(import('@backstage/plugin-auth-backend-module-gcp-iap-provider'));
## Adding the provider to the Backstage frontend
-See [Sign-In with Proxy Providers](../index.md#sign-in-with-proxy-providers) for pointers on how to set up the sign-in page, and to also make it work smoothly for local development. You'll use `gcp-iap` as the provider name.
+See [Sign-In with Proxy Providers](../index.md#sign-in-with-proxy-providers) for pointers on how to set up the sign-in page, and to also make it work smoothly for local development. You'll use `gcpIap` as the provider name.
If you [provide a custom sign in resolver](https://backstage.io/docs/auth/identity-resolver#building-custom-resolvers), you can skip the `signIn` block entirely.
diff --git a/docs/auth/identity-resolver.md b/docs/auth/identity-resolver.md
index 5efb592270..dbebf3056c 100644
--- a/docs/auth/identity-resolver.md
+++ b/docs/auth/identity-resolver.md
@@ -193,7 +193,7 @@ backend.add(import('@backstage/plugin-auth-backend-module-github-provider'));
backend.add(customAuth);
```
-Check out [the naming patterns article](../backend-system/architecture/07-naming-patterns.md) for what rules
+Check out [the naming patterns article](../backend-system/architecture/08-naming-patterns.md) for what rules
apply regarding how to form valid IDs. In this example we also put the module
declaration directly in `packages/backend/src/index.ts` but that's just for
simplicity. You can place it anywhere you like, including in other packages, and
@@ -403,3 +403,22 @@ const customAuth = createBackendModule({
```
Remember to `backend.add` the created module just like above.
+
+## Common Sign-In Resolver Errors
+
+There are two common Sign-In Resolver errors you might run into.
+
+First is: "The 'Auth Provider Name' provider is not configured to support sign-in". Here is what this looks like for the GitHub Auth provider:
+
+
+
+This error can be caused by the following:
+
+- The `signIn.resolvers` have not be added to your Auth Provider configuration. Adding this will resolve the error.
+- There is a syntax error in your Auth Provider configuration. Running `yarn backstage-cli config:check --strict` will help identify the syntax error.
+
+The second common error is: "Failed to sign-in, unable to resolve user identity". Here is what this looks like for the GitHub Auth provider:
+
+
+
+This error is caused by the Sign-In Resolver you configured being unable to find a matching User in the Catalog. To fix this you need to import User, and Group, data from some source of truth for this data at your Organization. To do this you can use one of the existing Org Data providers like the ones for [Entra ID (Azure AD/MS Graph)](../integrations/azure/org.md), [GitHub](../integrations/github/org.md), [GitLab](../integrations/gitlab/org.md), etc. or if none of those fit your needs you can create a [Custom Entity Provider](../features/software-catalog/external-integrations.md#custom-entity-providers).
diff --git a/docs/auth/index.md b/docs/auth/index.md
index 5188d7e2a4..de579d039a 100644
--- a/docs/auth/index.md
+++ b/docs/auth/index.md
@@ -112,9 +112,16 @@ const app = createApp({
});
```
-You can also use the `providers` prop to enable multiple sign-in methods, for example
+:::note Note
-- allowing guest access:
+You can configure sign-in to use a redirect flow with no pop-up by adding
+`enableExperimentalRedirectFlow: true` to the root of your `app-config.yaml`
+
+:::
+
+### Using Multiple Providers
+
+You can also use the `providers` prop to enable multiple sign-in methods, for example to allow guest access:
```tsx title="packages/app/src/App.tsx"
const app = createApp({
@@ -140,12 +147,53 @@ const app = createApp({
});
```
-:::note Note
+### Conditionally Render Sign In Provider
-You can configure sign-in to use a redirect flow with no pop-up by adding
-`enableExperimentalRedirectFlow: true` to the root of your `app-config.yaml`
+In the above example you have both Guest and GitHub sign-in options, this is helpful for non-production but in Production you will most likely not want to offer Guest access. You can easily use information from your config to help conditionally render the provider:
-:::
+```tsx title="packages/app/src/App.tsx"
+import {
+ configApiRef,
+ githubAuthApiRef,
+ useApi,
+} from '@backstage/core-plugin-api';
+
+const app = createApp({
+ components: {
+ SignInPage: props => {
+ const configApi = useApi(configApiRef);
+ if (configApi.getString('auth.environment') === 'development') {
+ return (
+
+ );
+ }
+ return (
+
+ );
+ },
+ },
+ // ..
+});
+```
## Sign-In with Proxy Providers
diff --git a/docs/auth/microsoft/provider.md b/docs/auth/microsoft/provider.md
index f799eeb627..60104b99b7 100644
--- a/docs/auth/microsoft/provider.md
+++ b/docs/auth/microsoft/provider.md
@@ -67,8 +67,6 @@ auth:
clientSecret: ${AZURE_CLIENT_SECRET}
tenantId: ${AZURE_TENANT_ID}
domainHint: ${AZURE_TENANT_ID}
- additionalScopes:
- - Mail.Send
signIn:
resolvers:
# typically you would pick one of these
@@ -86,7 +84,7 @@ The Microsoft provider is a structure with three mandatory configuration keys:
Leave blank if your app registration is multi tenant.
When specified, this reduces login friction for users with accounts in multiple tenants by automatically filtering away accounts from other tenants.
For more details, see [Home Realm Discovery](https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/home-realm-discovery-policy)
-- `additionalScopes` (optional): List of scopes for the App Registration. The default and mandatory value is ['user.read'].
+- `additionalScopes` (optional): List of scopes for the App Registration, to be requested in addition to the required ones.
### Resolvers
diff --git a/docs/backend-system/architecture/03-services.md b/docs/backend-system/architecture/03-services.md
index 95e3d5305a..57f6b20b73 100644
--- a/docs/backend-system/architecture/03-services.md
+++ b/docs/backend-system/architecture/03-services.md
@@ -38,7 +38,7 @@ export const fooServiceRef = createServiceRef({
The `fooServiceRef` that we create above should be exported, and can then be used to declare a dependency on the `FooService` interface and receive an implementation of it at runtime.
-When creating a service reference you need to give it an ID. This ID needs to be globally unique, and should generally be of the format `'.'`. For more naming patterns surrounding services, see the [naming patterns](./07-naming-patterns.md#services) page.
+When creating a service reference you need to give it an ID. This ID needs to be globally unique, and should generally be of the format `'.'`. For more naming patterns surrounding services, see the [naming patterns](./08-naming-patterns.md#services) page.
A note on naming: the frontend and backend systems intentionally use the separate names "APIs" and "Services" for concepts that are quite similar. This is to avoid confusion between the two, both in documentation and discussion, but also in code. While the two systems are quite similar, they are not identical, and they can't be used interchangeably.
diff --git a/docs/backend-system/architecture/04-plugins.md b/docs/backend-system/architecture/04-plugins.md
index af59c2a48a..5aef71e24f 100644
--- a/docs/backend-system/architecture/04-plugins.md
+++ b/docs/backend-system/architecture/04-plugins.md
@@ -10,7 +10,7 @@ Plugins provide the actual base features of a Backstage backend. Each plugin ope
## Defining a Plugin
-Plugins are created using the `createBackendPlugin` function, and should typically be exported from a plugin package. All plugins must have an ID and a `register` method, where the ID matches the plugin ID in the package name, without the `-backend` suffix. See also the [dedicated section](./07-naming-patterns.md) about proper naming patterns.
+Plugins are created using the `createBackendPlugin` function, and should typically be exported from a plugin package. All plugins must have an ID and a `register` method, where the ID matches the plugin ID in the package name, without the `-backend` suffix. See also the [dedicated section](./08-naming-patterns.md) about proper naming patterns.
```ts
// plugins/example-backend/src/plugin.ts
diff --git a/docs/backend-system/architecture/05-extension-points.md b/docs/backend-system/architecture/05-extension-points.md
index 4b7f893f0b..12c4ec75f5 100644
--- a/docs/backend-system/architecture/05-extension-points.md
+++ b/docs/backend-system/architecture/05-extension-points.md
@@ -78,7 +78,7 @@ Another pattern that can be used is a type of singleton pattern where the extens
```ts
interface ScaffolderTaskRunnerExtensionPoint {
- setTaskRunner(taskRunner: TaskRunner): void;
+ setTaskRunner(taskRunner: SchedulerServiceTaskRunner): void;
}
```
diff --git a/docs/backend-system/architecture/06-modules.md b/docs/backend-system/architecture/06-modules.md
index eb00e737e3..010f36109c 100644
--- a/docs/backend-system/architecture/06-modules.md
+++ b/docs/backend-system/architecture/06-modules.md
@@ -52,7 +52,7 @@ This allows you to install the module in your backend instance by just referenci
```ts
backend.add(
- import('backstage-plugin-catalog-backend-module-example-processor'),
+ import('@internal/backstage-plugin-catalog-backend-module-example-processor'),
);
```
diff --git a/docs/backend-system/architecture/07-feature-loaders.md b/docs/backend-system/architecture/07-feature-loaders.md
new file mode 100644
index 0000000000..cf52a1feb3
--- /dev/null
+++ b/docs/backend-system/architecture/07-feature-loaders.md
@@ -0,0 +1,101 @@
+---
+id: feature-loaders
+title: Backend Feature Loaders
+sidebar_label: Feature Loaders
+# prettier-ignore
+description: Backend feature loaders
+---
+
+Backend feature loaders are used to programmatically select and install features in a Backstage backend. They can service a wide range of use cases, such as enabling or disabling features based on static configuration, dynamically load features at runtime, or conditionally load features based on the state of a system.
+
+Feature loaders are defined using the `createBackendFeatureLoader` function, exported by `@backstage/backend-plugin-api`. It accepts a `loader` function, as well as an optional `deps` object for declaring service dependencies. Unlike plugins and modules, feature loaders are limited to only depending on root-scoped services, but that still allows access to for example the [root config](../core-services/root-config.md) and [root logger](../core-services/root-logger.md) services.
+
+The `loader` function can be defined in many different ways, with the main requirement being that it returns a list of `BackendFeature`s in some form. A backend feature is the kind of object that you can pass to `backend.add(...)`, for example services factories, plugins, modules, or even other feature loaders. The `loader` function can be synchronous or asynchronous, and can be defined as a generator function to allow for more complex logic.
+
+## Examples
+
+The following are a few example of how feature loaders can be used:
+
+### Simple list of features
+
+A feature loader can simply return a list of features to be installed:
+
+```ts
+export default createBackendFeatureLoader({
+ loader() {
+ return [
+ import('@backstage/plugin-search-backend/alpha'),
+ import('@backstage/plugin-search-backend-module-catalog/alpha'),
+ import('@backstage/plugin-search-backend-module-explore/alpha'),
+ import('@backstage/plugin-search-backend-module-techdocs/alpha'),
+ ];
+ },
+});
+```
+
+It can also encapsulate a collection of custom features:
+
+```ts
+export default createBackendFeatureLoader({
+ // Async loader is fine too
+ async loader() {
+ return [
+ createBackendPlugin({
+ ...
+ }),
+ createBackendModule({
+ ...
+ }),
+ ]
+ },
+});
+```
+
+### Conditional loading
+
+A feature loader can access root-scoped services, such as the config service. This allows for conditional loading of features based on configuration. It is often convenient to use a generator function for this purpose:
+
+```ts
+export default createBackendFeatureLoader({
+ deps: {
+ config: coreServices.rootConfig,
+ },
+ // The `*` in front of the function name makes it a generator function
+ *loader({ config }) {
+ // Example of a custom config flag to enable search
+ if (config.getOptionalString('customFeatureToggle.search')) {
+ yield import('@backstage/plugin-search-backend/alpha');
+ yield import('@backstage/plugin-search-backend-module-catalog/alpha');
+ yield import('@backstage/plugin-search-backend-module-explore/alpha');
+ yield import('@backstage/plugin-search-backend-module-techdocs/alpha');
+ }
+ },
+});
+```
+
+### Dynamic logic
+
+A feature loader can also be asynchronous, and for example fetch data from an external source to determine which features to load:
+
+```ts
+export default createBackendFeatureLoader({
+ // The `async *` in front of the function name makes it an async generator function.
+ async *loader() {
+ const localMetadata = await readMetadataFromDisk();
+
+ if (localMetadata.enableSearch) {
+ yield import('@backstage/plugin-search-backend/alpha');
+ yield import('@backstage/plugin-search-backend-module-catalog/alpha');
+
+ const remoteMetadata = await fetchMetadata();
+
+ if (remoteMetadata.enableExplore) {
+ yield import('@backstage/plugin-search-backend-module-explore/alpha');
+ }
+ if (remoteMetadata.enableTechDocs) {
+ yield import('@backstage/plugin-search-backend-module-techdocs/alpha');
+ }
+ }
+ },
+});
+```
diff --git a/docs/backend-system/architecture/07-naming-patterns.md b/docs/backend-system/architecture/08-naming-patterns.md
similarity index 100%
rename from docs/backend-system/architecture/07-naming-patterns.md
rename to docs/backend-system/architecture/08-naming-patterns.md
diff --git a/docs/backend-system/building-backends/08-migrating.md b/docs/backend-system/building-backends/08-migrating.md
index c6db44078e..67104c7ba9 100644
--- a/docs/backend-system/building-backends/08-migrating.md
+++ b/docs/backend-system/building-backends/08-migrating.md
@@ -497,7 +497,7 @@ catalog:
providers:
githubOrg:
- id: production
- githubUrl: 'https://github.com',
+ githubUrl: 'https://github.com'
orgs: ['backstage']
schedule:
frequency: PT30M
@@ -536,8 +536,8 @@ catalog:
providers:
githubOrg:
- id: production
- githubUrl: 'https://github.com',
- orgs: ['org-a', 'org-b'],
+ githubUrl: 'https://github.com'
+ orgs: ['org-a', 'org-b']
schedule:
frequency: PT30M
timeout: PT15M
diff --git a/docs/backend-system/building-plugins-and-modules/01-index.md b/docs/backend-system/building-plugins-and-modules/01-index.md
index 379ce19587..4dfca00d4e 100644
--- a/docs/backend-system/building-plugins-and-modules/01-index.md
+++ b/docs/backend-system/building-plugins-and-modules/01-index.md
@@ -68,7 +68,7 @@ that's specific to your plugin. In the example above, the logger might tag
messages with your plugin ID, and the HTTP router might prefix API routes with
your plugin ID, depending on the implementation used.
-See [the article on naming patterns](../architecture/07-naming-patterns.md) for
+See [the article on naming patterns](../architecture/08-naming-patterns.md) for
details on how to best choose names/IDs for plugins and related backend system
items.
@@ -124,7 +124,7 @@ export const catalogModuleExampleCustomProcessor = createBackendModule({
export { catalogModuleExampleCustomProcessor as default } from './module';
```
-See [the article on naming patterns](../architecture/07-naming-patterns.md) for
+See [the article on naming patterns](../architecture/08-naming-patterns.md) for
details on how to best choose names/IDs for modules and related backend system
items.
diff --git a/docs/backend-system/building-plugins-and-modules/08-migrating.md b/docs/backend-system/building-plugins-and-modules/08-migrating.md
index 699852c494..8b2ea17de8 100644
--- a/docs/backend-system/building-plugins-and-modules/08-migrating.md
+++ b/docs/backend-system/building-plugins-and-modules/08-migrating.md
@@ -255,3 +255,67 @@ backend.add(
Checkout the [custom service implementations](https://backstage.io/docs/backend-system/building-backends/index#custom-service-implementations) documentation and also the [core service configurations](https://backstage.io/docs/backend-system/core-services/index) page in case you'd like to create your own custom mock factory for one or more services.
3. Now you can finally start your plugin locally by running `yarn start` from the root folder of your plugin.
+
+## Remove support for the old backend system
+
+Given that you have followed the guide above to export your new backend plugin the steps to deprecate and remove the old backend plugin are the following:
+
+### Deprecate public exports other than the default export
+
+First of all make sure that `createRouter` and `routerOptions` are marked as deprecated to give users time and an indication to migrate to the new system (we recommend deprecating in one release and remove the deprecates in the following one).
+This is done by adding a `@deprecated` annotation to the legacy exports. It's worth noting that the plugin can continue using `createRouter` internally but it should not be exported as part of public api. If you are reusing the create router and relative imports in migrated plugins, ensure that you refactor the internal code to remove deprecated imports once the `createRouter` export gets deleted. It is recommended that you avoid the use of `@backstage/backend-common` and `@backstage/backend-tasks` in migrated plugins as they will be deleted together with the ending of support for the legacy system. There are instructions in most of the deprecated imports about how to stop using them once you have migrated to the new backend system.
+
+```ts title="@backstage/plugin-kubernetes-backend/src/service/router.ts"
+import { KubernetesBuilder } from './KubernetesBuilder';
+
+/**
+* @public
+// highlight-add-next-line
+* @deprecated Please migrate to the new backend system.
+*/
+export interface RouterOptions {
+ logger: Logger;
+ config: Config;
+ catalogApi: CatalogApi;
+ clusterSupplier?: KubernetesClustersSupplier;
+ discovery: PluginEndpointDiscovery;
+}
+
+/**
+* @public
+// highlight-add-next-line
+* @deprecated Please migrate to the new backend system.
+*/
+export async function createRouter(
+ options: RouterOptions,
+): Promise {
+ const { router } = await KubernetesBuilder.createBuilder(options)
+ .setClusterSupplier(options.clusterSupplier)
+ .build();
+ return router;
+}
+```
+
+If your plugin contains an `api-report.md` file make sure to run `yarn build:api-reports` afterwards.
+It's recommended to inspect the api report and look for other exports other than the new backend plugin, they should most likely also be deprecated as plugins in the new backend system are extended using extension points and not directly by passing options. Any type of builder or helper methods that are used together with the backend plugin should be moved to a library package specifically for that plugin (e.g. a `plugin-kubernetes-backend-node` package, see the [package roles](https://backstage.io/docs/tooling/cli/build-system/#package-roles) documentation for more details).
+
+After removals of deprecations all your `index.ts` should contain is just the default export:
+
+```ts title="@backstage/plugin-kubernetes-backend/src/index.ts"
+export { kubernetesPlugin as default } from './plugin';
+```
+
+### Deprecate the `/alpha` subpath if it exists
+
+In cases where you previously supported the new backend system using an `alpha` export, please deprecate the alpha exports and re-export them from `index.ts`.
+
+```ts title="@backstage/-backend/src/alpha.ts"
+/**
+* @alpha
+// highlight-add-next-line
+* @deprecated Please import from the root path instead.
+*/
+export default createPlugin({
+ //...
+});
+```
diff --git a/docs/backend-system/core-services/root-http-router.md b/docs/backend-system/core-services/root-http-router.md
index b3522e1752..88a10455f5 100644
--- a/docs/backend-system/core-services/root-http-router.md
+++ b/docs/backend-system/core-services/root-http-router.md
@@ -52,19 +52,49 @@ You can configure the root HTTP Router service by passing the options to the `cr
```ts
import { rootHttpRouterServiceFactory } from '@backstage/backend-app-api';
+import { RequestHandler } from 'express';
+import morgan from 'morgan';
const backend = createBackend();
backend.add(
rootHttpRouterServiceFactory({
configure: ({ app, middleware, routes, config, logger, lifecycle }) => {
+ // Refer to https://expressjs.com/en/guide/writing-middleware.html on how to write express middleware
+ const customMiddleware = {
+ logging(): RequestHandler {
+ const middlewareLogger = logger.child({
+ type: 'incomingRequest',
+ });
+ return (req, res, next) => {
+ // Custom Logging Implementation
+ next();
+ };
+ },
+ // Default logging middleware uses the [morgan](https://github.com/expressjs/morgan) middleware which you can configure with custom formats.
+ morganLogging(): RequestHandler {
+ const middlewareLogger = logger.child({
+ type: 'incomingRequest',
+ });
+ const customMorganFormat =
+ '[:date[clf]] ":method :url HTTP/:http-version" :status ":user-agent"';
+ return morgan(customMorganFormat, {
+ stream: {
+ write(message: string) {
+ logger.info(message.trimEnd());
+ },
+ },
+ });
+ },
+ };
+
// the built in middleware is provided through an option in the configure function
app.use(middleware.helmet());
app.use(middleware.cors());
app.use(middleware.compression());
// you can add you your own middleware in here
- app.use(custom.logging());
+ app.use(customMiddleware.logging());
// here the routes that are registered by other plugins
app.use(routes);
diff --git a/docs/backend-system/core-services/root-logger.md b/docs/backend-system/core-services/root-logger.md
index 733b7b8277..36bb276920 100644
--- a/docs/backend-system/core-services/root-logger.md
+++ b/docs/backend-system/core-services/root-logger.md
@@ -17,7 +17,8 @@ The following example is how you can override the root logger service to add add
```ts
import { coreServices } from '@backstage/backend-plugin-api';
-import { WinstonLogger } from '@backstage/backend-app-api';
+import { WinstonLogger } from '@backstage/backend-defaults/rootLogger';
+import { createConfigSecretEnumerator } from '@backstage/backend-defaults/rootConfig';
const backend = createBackend();
@@ -43,6 +44,12 @@ backend.add(
transports: [new transports.Console()],
});
+ const secretEnumerator = await createConfigSecretEnumerator({
+ logger,
+ });
+ logger.addRedactions(secretEnumerator(config));
+ config.subscribe?.(() => logger.addRedactions(secretEnumerator(config)));
+
return logger;
},
}),
diff --git a/docs/deployment/docker.md b/docs/deployment/docker.md
index 154f68c7d1..7b71c2eb61 100644
--- a/docs/deployment/docker.md
+++ b/docs/deployment/docker.md
@@ -81,7 +81,7 @@ USER node
WORKDIR /app
# This switches many Node.js dependencies to production mode.
-ENV NODE_ENV production
+ENV NODE_ENV=production
# Copy repo skeleton first, to avoid unnecessary docker cache invalidation.
# The skeleton contains the package.json of each package in the monorepo,
@@ -92,11 +92,14 @@ RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz
RUN --mount=type=cache,target=/home/node/.cache/yarn,sharing=locked,uid=1000,gid=1000 \
yarn install --frozen-lockfile --production --network-timeout 300000
+# This will include the examples, if you don't need these simply remove this line
+COPY --chown=node:node examples ./examples
+
# 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 ./
RUN tar xzf bundle.tar.gz && rm bundle.tar.gz
-CMD ["node", "packages/backend", "--config", "app-config.yaml"]
+CMD ["node", "packages/backend", "--config", "app-config.yaml", "--config", "app-config.production.yaml"]
```
For more details on how the `backend:bundle` command and the `skeleton.tar.gz`
@@ -243,6 +246,9 @@ WORKDIR /app
# 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/ ./
+# Note: The skeleton bundle only includes package.json files -- if your app has
+# plugins that define a `bin` export, the bin files need to be copied as well to
+# be linked in node_modules/.bin during yarn install.
RUN --mount=type=cache,target=/home/node/.cache/yarn,sharing=locked,uid=1000,gid=1000 \
yarn install --frozen-lockfile --production --network-timeout 600000
@@ -251,12 +257,15 @@ RUN --mount=type=cache,target=/home/node/.cache/yarn,sharing=locked,uid=1000,gid
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 ./
+COPY --chown=node:node app-config*.yaml ./
+
+# This will include the examples, if you don't need these simply remove this line
+COPY --chown=node:node examples ./examples
# This switches many Node.js dependencies to production mode.
-ENV NODE_ENV production
+ENV NODE_ENV=production
-CMD ["node", "packages/backend", "--config", "app-config.yaml"]
+CMD ["node", "packages/backend", "--config", "app-config.yaml", "--config", "app-config.production.yaml"]
```
Note that a newly created Backstage app will typically not have a `plugins/`
@@ -277,6 +286,7 @@ packages/*/dist
packages/*/node_modules
plugins/*/dist
plugins/*/node_modules
+*.local.yaml
```
Once you have added both the `Dockerfile` and `.dockerignore` to the root of
@@ -312,14 +322,12 @@ first step in doing so is to remove the `app-backend` plugin from the backend
package, which is done as follows:
1. Delete `packages/backend/src/plugins/app.ts`
-2. Remove the following lines from `packages/backend/src/index.ts`:
- ```tsx
- import app from './plugins/app';
- // ...
- const appEnv = useHotMemoize(module, () => createEnv('app'));
- // ...
- .addRouter('', await app(appEnv));
+2. Remove the following line from `packages/backend/src/index.ts`:
+
+ ```ts
+ backend.add(import('@backstage/plugin-app-backend/alpha'));
```
+
3. Remove the `@backstage/plugin-app-backend` and the app package dependency
(e.g. `app`) from `packages/backend/package.json`. If you don't remove the
app package dependency the app will still be built and bundled with the
diff --git a/docs/deployment/flightcontrol.md b/docs/deployment/flightcontrol.md
index 5d59371b47..14e0b308a6 100644
--- a/docs/deployment/flightcontrol.md
+++ b/docs/deployment/flightcontrol.md
@@ -68,7 +68,7 @@ USER node
WORKDIR /app
# This switches many Node.js dependencies to production mode.
-ENV NODE_ENV production
+ENV NODE_ENV=production
# Copy repo skeleton first, to avoid unnecessary docker cache invalidation.
# The skeleton contains the package.json of each package in the monorepo,
diff --git a/docs/deployment/heroku.md b/docs/deployment/heroku.md
index 1545c34733..51d0b4b439 100644
--- a/docs/deployment/heroku.md
+++ b/docs/deployment/heroku.md
@@ -5,40 +5,37 @@ sidebar_label: Heroku
description: How to deploy Backstage to Heroku
---
-Heroku is a Platform as a Service (PaaS) designed to handle application
-deployment in a hands-off way. Heroku supports container deployment of Docker
-images, a natural fit for Backstage.
+Heroku is a Platform as a Service (PaaS) designed to simplify application deployment.
-## Configuring the CLI
+## Create App
-First, install the
-[heroku-cli](https://devcenter.heroku.com/articles/heroku-cli) and login:
+Starting with an existing Backstage app or follow the [getting started guide](https://backstage.io/docs/getting-started/) to create a new one.
+
+Install the
+[Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) and create a new Heroku app:
```shell
-$ heroku login
+cd your-app/
+heroku apps:create
```
-If you have not yet created a project through the Heroku interface, you can create it through the CLI.
+## Domain
+
+Get Heroku app URL:
```shell
-$ heroku create
+heroku domains -a
+.herokuapp.com
```
-You _might_ also need to set your Heroku app's stack to `container`:
-
-```bash
-$ heroku stack:set container -a
-```
-
-Configuring your `app-config.yaml`:
+The core [app-backend plugin](https://www.npmjs.com/package/@backstage/plugin-app-backend) allows a single Heroku app to serve the frontend and backend. To make this work you need to update the `baseUrl` and `port` in `app-config.production.yaml`:
```yaml
app:
- # Should be the same as backend.baseUrl when using the `app-backend` plugin
- baseUrl: https://.herokuapp.com
+ baseUrl: https://.herokuapp.com
backend:
- baseUrl: https://.herokuapp.com
+ baseUrl: https://.herokuapp.com
listen:
port:
$env: PORT
@@ -46,28 +43,98 @@ backend:
# https://devcenter.heroku.com/articles/dynos#web-dynos
```
-> Make sure your file is being copied into your container in the `Dockerfile`.
+## Build Script
-Before building the Docker image, run the [backstage host build commands](https://backstage.io/docs/deployment/docker#host-build). They must be run whenever you are going to publish a new image.
+Add a build script in `package.json` to compile frontend during deployment:
-Heroku runs a container registry on `registry.heroku.com`. To push Backstage
-Docker images, log in to the container registry also:
+```json
+"scripts": {
+ "build": "yarn build:backend --config ../../app-config.yaml --config ../../app-config.production.yaml"
+```
+
+## Start Command
+
+Create a [Procfile](https://devcenter.heroku.com/articles/procfile) in the app's root:
```shell
-$ heroku container:login
+echo "web: yarn workspace backend start --config ../../app-config.yaml --config ../../app-config.production.yaml" > Procfile
```
-## Push and deploy a Docker image
+## Database
-Now we can push a Backstage [Docker image](docker.md) to Heroku's container
-registry and release it to the `web` worker:
+Provision a [Heroku Postgres](https://elements.heroku.com/addons/heroku-postgresql) database:
-```bash
-$ docker image build . -f packages/backend/Dockerfile --tag registry.heroku.com//web
-
-$ docker push registry.heroku.com//web
-
-$ heroku container:release web -a
+```shell
+heroku addons:create heroku-postgresql -a
```
-Now you should have Backstage up and running! 🎉
+Update `database` in `app-config.production.yaml`:
+
+```yaml
+backend:
+ database:
+ client: pg
+ pluginDivisionMode: schema
+ ensureExists: false
+ ensureSchemaExists: true
+ connection: ${DATABASE_URL}
+```
+
+Allow postgres self-signed certificates:
+
+```shell
+heroku config:set PGSSLMODE=no-verify -a
+```
+
+## Deployment
+
+Commit changes and push to Heroku to build and deploy:
+
+```shell
+git add Procfile && git commit -am "configure heroku"
+git push heroku main
+```
+
+View the app in the browser:
+
+```shell
+heroku open -a
+```
+
+View logs:
+
+```shell
+heroku logs -a
+```
+
+## Docker
+
+As an alternative to git deploys, Heroku also [supports container images](https://devcenter.heroku.com/articles/container-registry-and-runtime).
+
+Login to Heroku's container registry:
+
+```shell
+heroku container:login
+```
+
+Configure the Heroku app to run a container image:
+
+```shell
+heroku stack:set container -a
+```
+
+Locally run the [host build commands](https://backstage.io/docs/deployment/docker/#host-build), they must be run whenever you are going to publish a new image:
+
+```shell
+yarn install --frozen-lockfile
+yarn tsc
+yarn build:backend --config ../../app-config.yaml --config ../../app-config.production.yaml
+```
+
+Build, push, and release the container image to the `web` dyno:
+
+```shell
+docker image build . -f packages/backend/Dockerfile --tag registry.heroku.com//web
+docker push registry.heroku.com//web
+heroku container:release web -a
+```
diff --git a/docs/features/search/collators.md b/docs/features/search/collators.md
new file mode 100644
index 0000000000..27860bfd64
--- /dev/null
+++ b/docs/features/search/collators.md
@@ -0,0 +1,107 @@
+---
+id: collators
+title: Collators
+description: Indexing you Backstage content with Collators
+---
+
+Backstage includes 2 [collators](./concepts.md#collators) out of the box for the [Catalog](#catalog) and [TechDocs](#techdocs). There's also some from the [Backstage Community](#community-collators) too!
+
+## Catalog
+
+The Catalog collator will index all the Entities in your Catalog. It is installed by default but if you need to add it manually here's how.
+
+First we add the plugin into your backend app:
+
+```bash title="From your Backstage root directory"
+yarn --cwd packages/backend add @backstage/plugin-search-backend-module-catalog
+```
+
+Then add the following line:
+
+```ts title="packages/backend/src/index.ts"
+const backend = createBackend();
+
+// Other plugins...
+
+// search plugin
+backend.add(import('@backstage/plugin-search-backend/alpha'));
+
+/* highlight-add-start */
+backend.add(import('@backstage/plugin-search-backend-module-catalog/alpha'));
+/* highlight-add-end */
+
+backend.start();
+```
+
+### Configuring the Catalog Collator
+
+The default schedule for the Catalog Collator is to run every 10 minutes, you can provide your own schedule by adding it to your config:
+
+```yaml title="app-config.yaml
+search:
+ collators:
+ catalog:
+ schedule: # same options as in SchedulerServiceTaskScheduleDefinition
+ # supports cron, ISO duration, "human duration" as used in code
+ initialDelay: { seconds: 90 }
+ # supports cron, ISO duration, "human duration" as used in code
+ frequency: { hours: 6 }
+ # supports ISO duration, "human duration" as used in code
+ timeout: { minutes: 3 }
+```
+
+## TechDocs
+
+The TechDocs collator will index all the TechDocs in your Catalog. It is installed by default but if you need to add it manually here's how.
+
+First we add the plugin into your backend app:
+
+```bash title="From your Backstage root directory"
+yarn --cwd packages/backend add @backstage/plugin-search-backend-module-techdocs
+```
+
+Then add the following line:
+
+```ts title="packages/backend/src/index.ts"
+const backend = createBackend();
+
+// Other plugins...
+
+// search plugin
+backend.add(import('@backstage/plugin-search-backend/alpha'));
+
+/* highlight-add-start */
+backend.add(import('@backstage/plugin-search-backend-module-techdocs/alpha'));
+/* highlight-add-end */
+
+backend.start();
+```
+
+### Configuring the TechDocs Collator
+
+The default schedule for the TechDocs Collator is to run every 10 minutes, you can provide your own schedule by adding it to your config:
+
+```yaml title="app-config.yaml
+search:
+ collators:
+ techdocs:
+ schedule: # same options as in SchedulerServiceTaskScheduleDefinition
+ # supports cron, ISO duration, "human duration" as used in code
+ initialDelay: { seconds: 90 }
+ # supports cron, ISO duration, "human duration" as used in code
+ frequency: { hours: 6 }
+ # supports ISO duration, "human duration" as used in code
+ timeout: { minutes: 3 }
+```
+
+## Community Collators
+
+Here are some of the known Search Collators available in from the Backstage Community:
+
+- [`@backstage/plugin-search-backend-module-explore`](https://github.com/backstage/backstage/tree/master/plugins/search-backend-module-explore): will index content from the [Explore plugin](https://github.com/backstage/community-plugins/tree/main/workspaces/explore/plugins/explore).
+- [`@backstage/plugin-search-backend-module-stack-overflow-collator`](https://github.com/backstage/backstage/tree/master/plugins/search-backend-module-stack-overflow-collator): will index content from Stack Overflow.
+- [`@backstage-community/search-backend-module-adr`](https://github.com/backstage/community-plugins/tree/main/workspaces/adr/plugins/search-backend-module-adr): will index content from the [ADR plugin](https://github.com/backstage/community-plugins/tree/main/workspaces/adr/plugins/adr).
+
+## Custom Collators
+
+To create your own collators/decorators modules, please use the [searchModuleCatalogCollator](https://github.com/backstage/backstage/blob/d7f955f300893f50c4882ea8f5c09aa42dfaacfd/plugins/search-backend-module-catalog/src/alpha.ts#L49) as an example, we recommend that modules are separated by plugin packages (e.g. `search-backend-module-`). You can also find the available search engines and collator/decorator modules documentation in the Alpha API reports.
diff --git a/docs/features/search/concepts.md b/docs/features/search/concepts.md
index 69a7223085..a777c2f53c 100644
--- a/docs/features/search/concepts.md
+++ b/docs/features/search/concepts.md
@@ -86,7 +86,7 @@ Search chooses to completely rebuild indices on a schedule. Different collators
can be configured to refresh at different intervals, depending on how often the
source information is updated. When search indexing is distributed among multiple
backend nodes, coordination to prevent clashes is typically handled by a
-distributed `TaskRunner`.
+distributed `SchedulerServiceTaskRunner`.
### The Search Page
diff --git a/docs/features/search/getting-started.md b/docs/features/search/getting-started.md
index b2a074974f..362b7fc46d 100644
--- a/docs/features/search/getting-started.md
+++ b/docs/features/search/getting-started.md
@@ -133,79 +133,34 @@ For more information about using `Root.tsx`, please see
Add the following plugins into your backend app:
```bash title="From your Backstage root directory"
-yarn --cwd packages/backend add @backstage/plugin-search-backend @backstage/plugin-search-backend-node
+yarn --cwd packages/backend add @backstage/plugin-search-backend @backstage/plugin-search-backend-module-pg @backstage/plugin-search-backend-module-catalog @backstage/plugin-search-backend-module-techdocs
```
-Create a `packages/backend/src/plugins/search.ts` file containing the following
-code:
+Then add the following lines:
-```typescript
-import { useHotCleanup } from '@backstage/backend-common';
-import { createRouter } from '@backstage/plugin-search-backend';
-import {
- IndexBuilder,
- LunrSearchEngine,
-} from '@backstage/plugin-search-backend-node';
-import { PluginEnvironment } from '../types';
-import { DefaultCatalogCollator } from '@backstage/plugin-catalog-backend';
-import { Router } from 'express';
+```ts title="packages/backend/src/index.ts"
+const backend = createBackend();
-export default async function createPlugin(
- env: PluginEnvironment,
-): Promise {
- const searchEngine = new LunrSearchEngine({
- logger: env.logger,
- });
- const indexBuilder = new IndexBuilder({
- logger: env.logger,
- searchEngine,
- });
+// Other plugins...
- const every10MinutesSchedule = env.scheduler.createScheduledTaskRunner({
- frequency: { minutes: 10 },
- timeout: { minutes: 15 },
- initialDelay: { seconds: 3 },
- });
+/* highlight-add-start */
+// search plugin
+backend.add(import('@backstage/plugin-search-backend/alpha'));
- indexBuilder.addCollator({
- schedule: every10MinutesSchedule,
- factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
- discovery: env.discovery,
- tokenManager: env.tokenManager,
- }),
- });
+// search engines
+backend.add(import('@backstage/plugin-search-backend-module-pg/alpha'));
- const { scheduler } = await indexBuilder.build();
+// search collators
+backend.add(import('@backstage/plugin-search-backend-module-catalog/alpha'));
+backend.add(import('@backstage/plugin-search-backend-module-techdocs/alpha'));
+/* highlight-add-end */
- scheduler.start();
- useHotCleanup(module, () => scheduler.stop());
-
- return await createRouter({
- engine: indexBuilder.getSearchEngine(),
- logger: env.logger,
- });
-}
+backend.start();
```
-Make the following modifications to your `packages/backend/src/index.ts` file:
+With the above setup Search will use the [Lunr](https://github.com/olivernn/lunr.js) in-memory Search Engine but if your have Postgres setup as your database then it will use Postgres as your Search Engine. Learn more in the [Search Engines](./search-engines.md) documentation.
-Import the `plugins/search` file you created above:
-
-```typescript
-import search from './plugins/search';
-```
-
-Set up an environment for search:
-
-```typescript
-const searchEnv = useHotMemoize(module, () => createEnv('search'));
-```
-
-Register the search service with the router:
-
-```typescript
-apiRouter.use('/search', await search(searchEnv));
-```
+The above also sets up two Collators for you - Catalog and TechDocs - which will index content from these two locations so that you can easily search them. Learn more in the [Collators documentation](./collators.md).
## Customizing Search
@@ -328,7 +283,7 @@ Backstage Search builds and maintains its index
[on a schedule](./concepts.md#the-scheduler). You can change how often the
indexes are rebuilt for a given type of document. You may want to do this if
your documents are updated more or less frequently. You can do so by configuring
-a scheduled `TaskRunner` to pass into the `schedule` value, like this:
+a scheduled `SchedulerServiceTaskRunner` to pass into the `schedule` value, like this:
```typescript {3}
const every10MinutesSchedule = env.scheduler.createScheduledTaskRunner({
@@ -347,16 +302,19 @@ indexBuilder.addCollator({
```
Note: if you are using the in-memory Lunr search engine, you probably want to
-implement a non-distributed `TaskRunner` like the following to ensure consistency
+implement a non-distributed `SchedulerServiceTaskRunner` like the following to ensure consistency
if you're running multiple search backend nodes (alternatively, you can configure
the search plugin to use a non-distributed database such as
[SQLite](../../tutorials/configuring-plugin-databases.md#postgresql-and-sqlite-3)):
```typescript
-import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks';
+import {
+ SchedulerServiceTaskRunner,
+ SchedulerServiceTaskInvocationDefinition,
+} from '@backstage/backend-plugin-api';
-const schedule: TaskRunner = {
- run: async (task: TaskInvocationDefinition) => {
+const schedule: SchedulerServiceTaskRunner = {
+ run: async (task: SchedulerServiceTaskInvocationDefinition) => {
const startRefresh = async () => {
while (!task.signal?.aborted) {
try {
diff --git a/docs/features/search/how-to-guides.md b/docs/features/search/how-to-guides.md
index 9ce30da556..b0df9440f3 100644
--- a/docs/features/search/how-to-guides.md
+++ b/docs/features/search/how-to-guides.md
@@ -45,75 +45,6 @@ to do that in two steps.
});
```
-## How to index TechDocs documents
-
-The TechDocs plugin has supported integrations to Search, meaning that it
-provides a default collator factory ready to be used.
-
-The purpose of this guide is to walk you through how to register the
-[DefaultTechDocsCollatorFactory](https://github.com/backstage/backstage/blob/1adc2c7/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts)
-in your App, so that you can get TechDocs documents indexed.
-
-If you have been through the
-[Getting Started with Search guide](https://backstage.io/docs/features/search/getting-started),
-you should have the `packages/backend/src/plugins/search.ts` file available. If
-so, you can go ahead and follow this guide - if not, start by going through the
-getting started guide.
-
-1. Import the `DefaultTechDocsCollatorFactory` from
- `@backstage/plugin-search-backend-module-techdocs`.
-
- ```typescript
- import { DefaultTechDocsCollatorFactory } from '@backstage/plugin-search-backend-module-techdocs';
- ```
-
-2. If there isn't an existing schedule you'd like to run the collator on, be
- sure to create it first. Something like...
-
- ```typescript
- import { Duration } from 'luxon';
-
- const every10MinutesSchedule = env.scheduler.createScheduledTaskRunner({
- frequency: Duration.fromObject({ seconds: 600 }),
- timeout: Duration.fromObject({ seconds: 900 }),
- initialDelay: Duration.fromObject({ seconds: 3 }),
- });
- ```
-
-3. Register the `DefaultTechDocsCollatorFactory` with the IndexBuilder.
-
- ```typescript
- indexBuilder.addCollator({
- schedule: every10MinutesSchedule,
- factory: DefaultTechDocsCollatorFactory.fromConfig(env.config, {
- discovery: env.discovery,
- logger: env.logger,
- tokenManager: env.tokenManager,
- }),
- });
- ```
-
-You should now have your TechDocs documents indexed to your search engine of
-choice!
-
-If you want your users to be able to filter down to the techdocs type when
-searching, you can update your `SearchPage.tsx` file in
-`packages/app/src/components/search` by adding `techdocs` to the list of values
-of the `SearchType` component.
-
-```tsx title="packages/app/src/components/search/SearchPage.tsx"
-
-
- {/* ... */}
-
-```
-
-> Check out the documentation around [integrating search into plugins](../../plugins/integrating-search-into-plugins.md#create-a-collator) for how to create your own collator.
-
## How to customize fields in the Software Catalog or TechDocs index
Sometimes, you might want to have the ability to control which data passes into the search index
@@ -388,38 +319,3 @@ export const SearchModal = ({ toggleModal }: { toggleModal: () => void }) => (
```
There are other more specific search results layout components that also accept result item extensions, check their documentation: [SearchResultList](https://backstage.io/storybook/?path=/story/plugins-search-searchresultlist--with-result-item-extensions) and [SearchResultGroup](https://backstage.io/storybook/?path=/story/plugins-search-searchresultgroup--with-result-item-extensions).
-
-## How to migrate your backend installation to use Search together with the new backend system
-
-Recently, the Backstage maintainers [announced the new Backend System](https://backstage.io/blog/2023/02/15/backend-system-alpha). The search plugins are now migrated to support the new backend system. In this guide you will learn how to update your backend set up.
-
-In "packages/backend/index.ts", install the search plugin [1], the search engine [2], and the search collators/decorators modules [3]:
-
-```ts
-const backend = createBackend();
-// [1] adding the search plugin to the backend
-backend.add(import('@backstage/plugin-search-backend/alpha'));
-// [2] (optional) the default search engine is Lunr, if you want to extend the search backend with another search engine.
-backend.add(
- import('@backstage/plugin-search-backend-module-elasticsearch/alpha'),
-);
-// [3] extending search with collator modules to start index documents, take in optional schedule parameters.
-backend.add(import('@backstage/plugin-search-backend-module-catalog/alpha'));
-backend.add(import('@backstage/plugin-search-backend-module-techdocs/alpha'));
-backend.add(import('@backstage/plugin-search-backend-module-explore/alpha'));
-
-backend.start();
-```
-
-To create your own collators/decorators modules, please use the [searchModuleCatalogCollator](https://github.com/backstage/backstage/blob/d7f955f300893f50c4882ea8f5c09aa42dfaacfd/plugins/search-backend-module-catalog/src/alpha.ts#L49) as an example, we recommend that modules are separated by plugin packages (e.g. `search-backend-module-`). You can also find the available search engines and collator/decorator modules documentation in the Alpha API reports:
-
-**Search engine modules**
-
-- Postgres [module](https://github.com/backstage/backstage/blob/d7f955f300893f50c4882ea8f5c09aa42dfaacfd/plugins/search-backend-module-pg/alpha-api-report.md);
-- Elasticsearch [module](https://github.com/backstage/backstage/blob/d7f955f300893f50c4882ea8f5c09aa42dfaacfd/plugins/search-backend-module-elasticsearch/alpha-api-report.md).
-
-**Search collator/decorator modules**
-
-- Catalog [module](https://github.com/backstage/backstage/blob/d7f955f300893f50c4882ea8f5c09aa42dfaacfd/plugins/search-backend-module-catalog/alpha-api-report.md);
-- Explore [module](https://github.com/backstage/backstage/blob/d7f955f300893f50c4882ea8f5c09aa42dfaacfd/plugins/search-backend-module-explore/alpha-api-report.md);
-- TechDocs [module](https://github.com/backstage/backstage/blob/d7f955f300893f50c4882ea8f5c09aa42dfaacfd/plugins/search-backend-module-techdocs/alpha-api-report.md).
diff --git a/docs/features/search/search-engines.md b/docs/features/search/search-engines.md
index 86b6244fb6..f768a9bde8 100644
--- a/docs/features/search/search-engines.md
+++ b/docs/features/search/search-engines.md
@@ -4,34 +4,31 @@ title: Search Engines
description: Choosing and configuring your search engine for Backstage
---
-Backstage supports 3 search engines by default, an in-memory engine called Lunr,
-Elasticsearch and Postgres. You can configure your own search engines by
-implementing the provided interface as mentioned in the
-[search backend documentation.](./getting-started.md#Backend)
-
-Provided search engine implementations have their own way of constructing
-queries, which may be something you want to modify. Alterations to the querying
-logic of a search engine can be made by providing your own implementation of a
-QueryTranslator interface. This modification can be done without touching
-provided search engines by using the exposed setter to set the modified query
-translator into the instance.
-
-```typescript
-const searchEngine = new LunrSearchEngine({ logger: env.logger });
-searchEngine.setTranslator(new MyNewAndBetterQueryTranslator());
-```
+Backstage supports 3 search engines by default, an in-memory engine called [Lunr](#lunr), [Postgres](#postgres)
+and [Elasticsearch](#elasticsearch).
## Lunr
-Lunr search engine is enabled by default for your backstage instance if you have
-not done additional changes to the scaffolded app.
+Lunr search engine is enabled by default for your Backstage instance if you have not done additional changes to the scaffolded app.
-Lunr can be instantiated like this:
+As Lunr is built into the Search backend plugin it can be added like this:
-```typescript
-// app/backend/src/plugins/search.ts
-const searchEngine = new LunrSearchEngine({ logger: env.logger });
-const indexBuilder = new IndexBuilder({ logger: env.logger, searchEngine });
+```bash title="From your Backstage root directory"
+yarn --cwd packages/backend add @backstage/plugin-search-backend
+```
+
+Then add the following line:
+
+```ts title="packages/backend/src/index.ts"
+const backend = createBackend();
+
+// Other plugins...
+
+/* highlight-add-start */
+backend.add(import('@backstage/plugin-search-backend/alpha'));
+/* highlight-add-end */
+
+backend.start();
```
:::note Note
@@ -45,34 +42,39 @@ other search engines instead.
## Postgres
-The Postgres based search engine only requires that postgres being configured as
+The Postgres based search engine only requires that Postgres being configured as
the database engine for Backstage. Therefore it targets setups that want to
avoid maintaining another external service like Elasticsearch. The search
provides decent results and performs well with ten thousands of indexed
-documents. The connection to postgres is established via the database manager
+documents. The connection to Postgres is established via the database manager
also used by other plugins.
> **Important**: The search plugin requires at least Postgres 12!
-To use the `PgSearchEngine`, make sure that you have a Postgres database
-configured and make the following changes to your backend:
+First we need to add the plugin:
-1. Add a dependency on `@backstage/plugin-search-backend-module-pg` to your
- backend's `package.json`.
-2. Initialize the search engine. It is recommended to initialize it with a
- fallback to the lunr search engine if you are running Backstage for
- development locally with SQLite:
-
-```typescript
-// In packages/backend/src/plugins/search.ts
-
-// Initialize a connection to a search engine.
-const searchEngine = (await PgSearchEngine.supported(env.database))
- ? await PgSearchEngine.fromConfig(env.config, { database: env.database })
- : new LunrSearchEngine({ logger: env.logger });
+```bash title="From your Backstage root directory"
+yarn --cwd packages/backend add @backstage/plugin-search-backend-module-pg
```
-## Optional Configuration
+Then add the following line:
+
+```ts title="packages/backend/src/index.ts"
+const backend = createBackend();
+
+// Other plugins...
+
+// search plugin
+backend.add(import('@backstage/plugin-search-backend/alpha'));
+
+/* highlight-add-start */
+backend.add(import('@backstage/plugin-search-backend-module-pg/alpha'));
+/* highlight-add-end */
+
+backend.start();
+```
+
+### Optional Configuration
The following is an example of the optional configuration that can be applied when using Postgres as the search backend. Currently this is mostly for just the highlight feature:
@@ -106,19 +108,32 @@ Backstage supports Elasticsearch (and OpenSearch) search engine connections,
indexing and querying out of the box. Available configuration options enable
usage of either AWS or Elastic.co hosted solutions, or a custom self-hosted solution.
-Similarly to Lunr above, Elasticsearch can be set up like this:
+Similarly to Postgres above, Elasticsearch can be set up as follows.
-```typescript
-// app/backend/src/plugins/search.ts
-const searchEngine = await ElasticSearchSearchEngine.fromConfig({
- logger: env.logger,
- config: env.config,
-});
-const indexBuilder = new IndexBuilder({ logger: env.logger, searchEngine });
+First we need to add the plugin:
+
+```bash title="From your Backstage root directory"
+yarn --cwd packages/backend add @backstage/plugin-search-backend-module-elasticsearch
```
-For the engine to be available, your backend package needs a dependency on
-package `@backstage/plugin-search-backend-module-elasticsearch`.
+Then add the following line:
+
+```ts title="packages/backend/src/index.ts"
+const backend = createBackend();
+
+// Other plugins...
+
+// search plugin
+backend.add(import('@backstage/plugin-search-backend/alpha'));
+
+/* highlight-add-start */
+backend.add(
+ import('@backstage/plugin-search-backend-module-elasticsearch/alpha'),
+);
+/* highlight-add-end */
+
+backend.start();
+```
Elasticsearch needs some additional configuration before it is ready to use
within your instance. The configuration options are documented in the
@@ -126,67 +141,12 @@ within your instance. The configuration options are documented in the
The underlying functionality uses either the official Elasticsearch client
version 7.x (meaning that Elasticsearch version 7 is the only one confirmed to
-be supported), or the OpenSearch client, when the `aws` or `opensearch `provider
+be supported), or the OpenSearch client, when the `aws` or `opensearch` provider
is configured.
-Should you need to create your own bespoke search experiences that require more
-than just a query translator (such as faceted search or Relay pagination), you
-can access the configuration of the search engine in order to create new
-Elasticsearch clients. The version of the client need not be the same as one
-used internally by the Elasticsearch engine plugin. For example:
+### Example configurations
-```typescript
-import { isOpenSearchCompatible } from '@backstage/plugin-search-backend-module-elasticsearch';
-import { Client as ElasticClient } from '@elastic/elasticsearch';
-import { Client as OpenSearchClient } from '@opensearch-project/opensearch';
-
-// Return an Elasticsearch client
-const esClient = searchEngine.newClient(options => {
- if (!isOpenSearchCompatible(options)) {
- return new ElasticClient(options);
- }
-
- throw new Error('Incompatible options');
-});
-
-// Return an OpenSearch client
-const osClient = searchEngine.newClient(options => {
- if (isOpenSearchCompatible(options)) {
- return new OpenSearchClient(options);
- }
-
- throw new Error('Incompatible options');
-});
-```
-
-#### Set custom index template
-
-The Elasticsearch engine gives you the ability to set a custom index template if needed.
-
-> Index templates define settings, mappings, and aliases that can be applied automatically to new indices.
-
-```typescript
-// app/backend/src/plugins/search.ts
-const searchEngine = await ElasticSearchSearchEngine.initialize({
- logger: env.logger,
- config: env.config,
-});
-
-searchEngine.setIndexTemplate({
- name: '',
- body: {
- index_patterns: [''],
- template: {
- mappings: {},
- settings: {},
- },
- },
-});
-```
-
-## Example configurations
-
-### AWS
+#### AWS
Using AWS hosted Elasticsearch the only configuration option needed is the URL
to the Elasticsearch service. The implementation assumes that environment
@@ -201,7 +161,7 @@ search:
node: https://my-backstage-search-asdfqwerty.eu-west-1.es.amazonaws.com
```
-### Elastic.co
+#### Elastic.co
Elastic Cloud hosted Elasticsearch uses a Cloud ID to determine the instance of
hosted Elasticsearch to connect to. Additionally, username and password needs to
@@ -218,7 +178,7 @@ search:
password: changeme
```
-### OpenSearch
+#### OpenSearch
OpenSearch can be self hosted for example with the [official docker image](https://hub.docker.com/r/opensearchproject/opensearch). The configuration requires only the node and authentication.
@@ -232,7 +192,7 @@ search:
password: changeme
```
-### Others
+#### Others
Other Elasticsearch instances can be connected to by using standard
Elasticsearch authentication methods and exposed URL, provided that the cluster
@@ -242,8 +202,6 @@ username/password or an API key. For more information how to create an API key,
see
[Elastic documentation on API keys](https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html).
-#### Configuration examples
-
##### With username and password
```yaml
@@ -273,8 +231,6 @@ you may get an error caused by limited `thread_pool` configuration. ( `429 Too M
In this case you need to decrease the batch size to index the resources to prevent this kind of error. You can easily decrease
or increase the batch size in your `app-config.yaml` using the `batchSize` option provided for Elasticsearch configuration.
-#### Configuration example
-
**Set batch size to 100**
```yaml
diff --git a/docs/features/software-catalog/configuration.md b/docs/features/software-catalog/configuration.md
index 17d5f49205..67382aa6ce 100644
--- a/docs/features/software-catalog/configuration.md
+++ b/docs/features/software-catalog/configuration.md
@@ -58,6 +58,20 @@ catalog:
target: ../../examples/all.yaml
```
+:::note
+There might be cases where you need to test some `file` configurations in a Docker container. In a case like this, as the backend is serving the frontend in a default setup, the path would be from the root. Also, you need to **make sure to copy your files into your container**.
+
+Using the example above that would look like this:
+
+```yaml
+catalog:
+ locations:
+ - type: file
+ target: ./examples/all.yaml
+```
+
+:::
+
### Integration Processors
Integrations may simply provide a mechanism to handle `url` location type for an
diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md
index 416eb27fbd..57c2283586 100644
--- a/docs/features/software-catalog/descriptor-format.md
+++ b/docs/features/software-catalog/descriptor-format.md
@@ -1243,6 +1243,18 @@ system belongs to, e.g. `artists`. This field is optional.
| --------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------- |
| [`Domain`](#kind-domain) (default) | Same as this entity, typically `default` | [`partOf`, and reverse `hasPart`](well-known-relations.md#partof-and-haspart) |
+### `spec.type` [optional]
+
+The type of system. There is currently no enforced set of values for this field,
+so it is left up to the adopting organization to choose a nomenclature that
+matches their catalog hierarchy. This field is optional.
+
+Some common values for this field could be:
+
+- `product`
+- `service`
+- `feature-set`
+
## Kind: Domain
Describes the following entity kind:
@@ -1303,6 +1315,18 @@ which the domain is a part, e.g. `audio`. This field is optional.
| --------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------- |
| [`Domain`](#kind-domain) (default) | Same as this entity, typically `default` | [`partOf`, and reverse `hasPart`](well-known-relations.md#partof-and-haspart) |
+### `spec.type` [optional]
+
+The type of domain. There is currently no enforced set of values for this field,
+so it is left up to the adopting organization to choose a nomenclature that
+matches their catalog hierarchy. This field is optional.
+
+Some common values for this field could be:
+
+- `product-area`
+- `product-group`
+- `bundle`
+
## Kind: Location
Describes the following entity kind:
diff --git a/docs/features/software-catalog/system-model.md b/docs/features/software-catalog/system-model.md
index ee7270a960..b93dde37d2 100644
--- a/docs/features/software-catalog/system-model.md
+++ b/docs/features/software-catalog/system-model.md
@@ -50,7 +50,7 @@ components need to be in a known machine-readable format so we can build further
tooling and analysis on top.
APIs have a visibility: they are either public (making them available for any
-other component to consume), restricted (only available to an allowlisted set of
+other component to consume), restricted (only available to an allowed set of
consumers), or private (only available within their system). As public APIs are
going to be the primary way interaction between components, Backstage supports
documenting, indexing and searching all APIs so we can browse them as
diff --git a/docs/features/software-templates/dry-run-testing.md b/docs/features/software-templates/dry-run-testing.md
new file mode 100644
index 0000000000..dfabad265e
--- /dev/null
+++ b/docs/features/software-templates/dry-run-testing.md
@@ -0,0 +1,79 @@
+---
+id: dry-run-testing
+title: Dry Run Testing
+description: How to enable and implement dry run testing in actions
+---
+
+Scaffolder templates can be tested using the dry run feature of scaffolder actions. This allows you to simulate the effects of running a scaffolder action without making any actual changes to your environment, for example creating a webhook in Github. Once dry run is enabled in the scaffolder action, you can add handling to actions you use in your scaffolder templates to define how an action should operate in a dry run scenario.
+
+## Enabling dry run testing
+
+To enable dry run for your scaffolder action you need to add `supportsDryRun: true` to the configuration object of `createTemplateAction` in the function where the behavior of your action is defined:
+
+```typescript
+export function exampleAction() {
+ return createTemplateAction<{
+ example: string;
+ }>({
+ id: 'action:example',
+ description: 'Example action',
+ schema: {
+ input: {
+ type: 'object',
+ properties: {
+ example: {
+ title: 'example',
+ type: 'string',
+ },
+ },
+ },
+ },
+ supportsDryRun: true,
+ async handler(ctx) {
+ ...
+ },
+ });
+}
+```
+
+## Adding handling for dry run
+
+To add handling for dry run functionality you need to add a check for `ctx.isDryRun` inside the handler of the configuration object which is being passed into `createTemplateAction` in the function where the behavior of your action is defined. Once the check is successful, you can perform the desired actions expected in a dry run, e.g. outputting non-sensitive inputs.
+
+```typescript
+async handler(ctx) {
+ ...
+
+ // If this is a dry run, log and return
+ if (ctx.isDryRun) {
+ ctx.logger.info(`Dry run complete`);
+ return;
+ }
+
+ ...
+ },
+```
+
+## Testing dry run handling
+
+You will also need to add tests for the dry run handling, for example:
+
+```typescript
+ it('should not perform action during dry run', async () => {
+ ...
+
+ // Create the context object with the necessary properties for a dry run
+ const ctx = {
+ ...mockContext,
+ isDryRun: true,
+ input: {
+ ...
+ },
+ };
+
+ // Call the handler with the context
+ await action.handler(ctx);
+
+ expect(...);
+ });
+```
diff --git a/docs/features/software-templates/ui-options-examples.md b/docs/features/software-templates/ui-options-examples.md
index 7f0e5b225f..1a024569a6 100644
--- a/docs/features/software-templates/ui-options-examples.md
+++ b/docs/features/software-templates/ui-options-examples.md
@@ -441,3 +441,30 @@ repoUrl:
`secretsKey` is the key used within the template secrets context to store the credential and `additionalScopes` is any additional permission scopes to request.
The supported `additionalScopes` values are `gerrit`, `github`, `gitlab`, `bitbucket`, and `azure`.
+
+## RepoBranchPicker
+
+The input props that can be specified under `ui:options` for the `RepoBranchPicker` field extension.
+
+### `requestUserCredentials`
+
+If defined will request user credentials to auth against the given SCM platform.
+
+```yaml
+repoUrl:
+ title: Repository Branch
+ type: string
+ ui:field: RepoBranchPicker
+ ui:options:
+ requestUserCredentials:
+ secretsKey: USER_OAUTH_TOKEN
+ additionalScopes:
+ github:
+ - workflow:write
+```
+
+`secretsKey` is the key used within the template secrets context to store the credential and `additionalScopes` is any additional permission scopes to request.
+
+The supported `additionalScopes` values are `gerrit`, `github`, `gitlab`, `bitbucket`, and `azure`.
+
+If you're also using the `RepoUrlPicker` field extension, you should simply duplicate this part from there.
diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md
index 075fe0bb08..13dbe6da4b 100644
--- a/docs/features/software-templates/writing-templates.md
+++ b/docs/features/software-templates/writing-templates.md
@@ -477,6 +477,34 @@ template can be published to multiple providers.
Note, that you will need to configure an [authentication provider](../../auth/index.md#configuring-authentication-providers), alongside the
[`ScmAuthApi`](../../auth/index.md#scaffolder-configuration-software-templates) for your source code management (SCM) service to make this feature work.
+### The Repository Branch Picker
+
+Similar to the repository picker, there is a picker for branches to support autocompletion. A full example could look like this:
+
+```yaml
+- title: Choose a branch
+ required:
+ - repoBranch
+ properties:
+ repoBranch:
+ title: Repository Branch
+ type: string
+ ui:field: RepoBranchPicker
+ ui:options:
+ requestUserCredentials:
+ secretsKey: USER_OAUTH_TOKEN
+```
+
+Passing the `requestUserCredentials` object is required for autocompletion to work.
+If you're also using the repository picker, you should simply duplicate this part from there.
+For more information regarding the `requestUserCredentials` object, please refer to the [Using the Users `oauth` token](#using-the-users-oauth-token) section under [The Repository Picker](#the-repository-picker).
+
+For a list of all possible `ui:options` input props for `RepoBranchPicker`, please visit [here](./ui-options-examples.md#repobranchpicker).
+
+The `RepoBranchPicker` is a custom field that we provide part of the
+`plugin-scaffolder`. You can provide your own custom fields by
+[writing your own Custom Field Extensions](./writing-custom-field-extensions.md)
+
### Accessing the signed-in users details
Sometimes when authoring templates, you'll want to access the user that is running the template, and get details from the profile or the users `Entity` in the Catalog.
diff --git a/docs/features/techdocs/configuration.md b/docs/features/techdocs/configuration.md
index a861a50c87..05f184e90f 100644
--- a/docs/features/techdocs/configuration.md
+++ b/docs/features/techdocs/configuration.md
@@ -20,7 +20,7 @@ techdocs:
# spin up the techdocs-container docker image or to run mkdocs locally (assuming all the dependencies are taken care of).
# You want to change this to 'local' if you are running Backstage using your own custom Docker setup and want to avoid running
# into Docker in Docker situation. Read more here
- # https://backstage.io/docs/features/techdocs/getting-started#disable-docker-in-docker-situation-optional
+ # https://backstage.io/docs/features/techdocs/getting-started/#disabling-docker-in-docker-situation-optional
runIn: 'docker'
@@ -203,7 +203,7 @@ techdocs:
# (Optional) The time (in milliseconds) that the TechDocs backend will wait
# for a cache service to respond before continuing on as though the cached
- # object was not found (e.g. when the cache sercice is unavailable). The
+ # object was not found (e.g. when the cache service is unavailable). The
# default value is 1000
readTimeout: 500
```
diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md
index 3a7cfd97bb..59198e919b 100644
--- a/docs/features/techdocs/how-to-guides.md
+++ b/docs/features/techdocs/how-to-guides.md
@@ -841,3 +841,8 @@ metadata:
annotations:
backstage.io/techdocs-entity: system:default/example
```
+
+## How to resolve broken links from moved or renamed pages in your documentation site
+
+TechDocs supports using the [mkdocs-redirects](https://github.com/mkdocs/mkdocs-redirects/tree/master) plugin to create a redirect map for any TechDocs site. This allows broken links from renamed or moved pages in your site to be redirected to their specified replacement.
+TechDocs will notify the user that the page they are trying to access is no longer maintained. Then, they will be redirected. External site redirects are not supported. If an external redirect is provided, the user will instead be redirected to the index page of the documentation site.
diff --git a/docs/frontend-system/architecture/01-index.md b/docs/frontend-system/architecture/00-index.md
similarity index 100%
rename from docs/frontend-system/architecture/01-index.md
rename to docs/frontend-system/architecture/00-index.md
diff --git a/docs/frontend-system/architecture/04-plugins.md b/docs/frontend-system/architecture/04-plugins.md
deleted file mode 100644
index 042d4fbc36..0000000000
--- a/docs/frontend-system/architecture/04-plugins.md
+++ /dev/null
@@ -1,93 +0,0 @@
----
-id: plugins
-title: Frontend Plugins
-sidebar_label: Plugins
-# prettier-ignore
-description: Frontend plugins
----
-
-> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
-
-## Introduction
-
-
-
-## Creating a Plugin
-
-
-
-```ts
-export const myPlugin = createPlugin({
- id: 'my-plugin',
-});
-```
-
-
-
-### Plugin ID
-
-
-
-### Plugin Extensions
-
-
-
-### Plugin Routes
-
-
-
-### Plugin External Routes
-
-
-
-### Plugin Feature Flags
-
-
-
-## Installing a Plugin in an App
-
-
diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md
deleted file mode 100644
index 41702e6823..0000000000
--- a/docs/frontend-system/architecture/05-extension-overrides.md
+++ /dev/null
@@ -1,170 +0,0 @@
----
-id: extension-overrides
-title: Frontend Extension Overrides
-sidebar_label: Extension Overrides
-# prettier-ignore
-description: Frontend extension overrides
----
-
-> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
-
-## Introduction
-
-An extension override is a building block of the frontend system that allows you to programmatically override app or plugin extensions anywhere in your application. Since the entire application is built mostly out of extensions from the bottom up, this is a powerful feature. You can use it for example to provide your own app root layout, to replace the implementation of a Utility API with a custom one, to override how the catalog page renders itself, and much more.
-
-In general, most features should have a good level of customization built into them, so that users do not have to leverage extension overrides to achieve common goals. A well written feature often has [configuration](../../conf/) settings, or uses extension inputs for extensibility where applicable. An example of this is the search plugin, which allows you to provide result renderers as inputs rather than replacing the result page wholesale just to tweak how results are shown. Adopters should take advantage of those when possible, and only use extension overrides when it's necessary to entirely replace the extension. Check the respective extension documentation for guidance.
-
-## Override App Extensions
-
-In order to override an app extension, you must create a new extension and add it to the list of overridden features. The steps are: create your extension overrides and use them in Backstage.
-
-### Example
-
-In the example below, we create a file that exports custom extensions for the app's `light` and `dark` themes:
-
-```tsx title="packages/app/src/themes.tsx"
-import {
- createThemeExtension,
- createExtensionOverrides,
-} from '@backstage/frontend-plugin-api';
-import { apertureThemes } from './themes';
-import { ApertureLightIcon, ApertureDarkIcon } from './icons';
-
-// Creating a light theme extension
-const apertureLightTheme = createThemeExtension({
- // highlight-start
- namespace: 'app',
- name: 'light',
- // highlight-end
- title: 'Aperture Light Theme',
- variant: 'light',
- icon: ,
- Provider: ({ children }) => (
-
- ),
-});
-
-// Creating a dark theme extension
-const apertureDarkTheme = createThemeExtension({
- // highlight-start
- namespace: 'app',
- name: 'dark',
- // highlight-end
- title: 'Aperture Dark Theme',
- variant: 'dark',
- icon: ,
- Provider: ({ children }) => (
-
- ),
-});
-
-// Creating an extension overrides preset
-export default createExtensionOverrides({
- extensions: [apertureLightTheme, apertureDarkTheme],
-});
-```
-
-Note that we declare `namespace` as `'app'` while creating the themes, so the system knows we are overriding app extensions. Additionally, to specifically override the `light` and `dark` theme extensions, we set the `name` option to `light` and `dark`. Therefore, to override app theme extensions, we ensure that the extension `namespace` and `name` match those of the default app theme extension definitions.
-
-Now we are able to use the overrides in a Backstage app:
-
-```tsx title="packages/app/src/App.tsx"
-import { createApp } from '@backstage/frontend-app-api';
-import themeOverrides from './themes';
-
-const app = createApp({
- // highlight-next-line
- features: [themeOverrides],
-});
-
-export default app.createRoot().
-```
-
-If the plugin you want to change is internal to your company or you just want to replace one of the application's core extensions, you can decide to store the overrides code directly in the app package or extract them to a separate package.
-
-Note that it can still be a good idea to split your overrides out into separate packages in large projects. But it's up to you to decide how to group the extensions into extension overrides.
-
-## Override Plugin Extensions
-
-To override an extension that is provided by a plugin, you need to provide a new extension that has the same ID as the existing extension. That is, all kind, namespace, and name options must match the extension you want to replace. This means that you typically need to provide an explicit `namespace` when overriding extensions from a plugin.
-
-:::info
-We recommend that plugin developers share the extension IDs in their plugin documentation, but usually you can infer the ID by following the [naming patterns](./08-naming-patterns.md) documentation.
-:::
-
-### Example
-
-Imagine you have a plugin with the ID `'search'`, and the plugin provides a page extension that you want to fully override with your own custom component. To do so, you need to create your page extension with an explicit `namespace` option that matches that of the plugin that you want to override, in this case `'search'`. If the existing extension also has an explicit `name` you'd need to set the `name` of your override extension to the same value as well.
-
-```tsx title="packages/app/src/search.tsx"
-import {
- createPageExtension,
- createExtensionOverrides,
-} from '@backstage/frontend-plugin-api';
-
-// Creating a custom search page extension
-const customSearchPage = createPageExtension({
- // highlight-next-line
- namespace: 'search',
- // Omitting name since it is the index plugin page
- defaultPath: '/search',
- loader: () => import('./SearchPage').then(m => m.),
-});
-
-export default createExtensionOverrides({
- extensions: [customSearchPage]
-});
-```
-
-Don't forget to configure your overrides in the `createApp` function:
-
-```tsx title="packages/app/src/App.tsx"
-import { createApp } from '@backstage/frontend-app-api';
-import searchOverrides from './search';
-
-const app = createApp({
- // highlight-next-line
- features: [searchOverrides],
-});
-
-export default app.createRoot();
-```
-
-Now let's talk about the last override case, orphan extensions.
-
-## Create Standalone Extensions
-
-Sometimes you just need to quickly create a new extension and not overwrite an app extension or plugin. You can also use overrides to create extensions, but remember that if you want to make this extension available for installation by other users, we recommend providing it via a plugin in a separate package.
-
-### Example
-
-Imagine you want to create a page that is currently only used by your application, like an Institutional page, for example. You can use overrides to extend the Backstage app to render it. To do so, simply create a page extension and pass it to the app as an override:
-
-```tsx title="packages/app/src/App.tsx"
-import { createApp } from '@backstage/frontend-app-api';
-import {
- createPageExtension,
- createExtensionOverrides,
-} from '@backstage/frontend-plugin-api';
-
-const app = createApp({
- features: [
- createExtensionOverrides({
- extensions: [
- // highlight-start
- createPageExtension({
- name: 'institutional',
- defaultPath: '/institutional',
- loader: () =>
- import('./institutional').then(m => ),
- }),
- // highlight-end
- ],
- }),
- ],
-});
-
-export default app.createRoot();
-```
-
-Note that we are omitting `namespace` when creating the page extension. When we omit `namespace`, we are telling the system the new extension is standalone and not an application or plugin extension!
diff --git a/docs/frontend-system/architecture/02-app.md b/docs/frontend-system/architecture/10-app.md
similarity index 95%
rename from docs/frontend-system/architecture/02-app.md
rename to docs/frontend-system/architecture/10-app.md
index 3b839d4232..532971d124 100644
--- a/docs/frontend-system/architecture/02-app.md
+++ b/docs/frontend-system/architecture/10-app.md
@@ -33,13 +33,13 @@ const rootEl = document.getElementById('root')!;
ReactDOM.createRoot(rootEl).render(app);
```
-We call `createApp` to create a new app instance, which is responsible for wiring together all of the features that we provide to the app. It also provides a set of built-in [Extensions](./03-extensions.md) that help build out the foundations of the app, as well as defaults for many other systems such as [Utility API](./06-utility-apis.md) implementations, components, icons, themes, and how to load configuration. No real work is done at the point of creating the app though, it's all deferred to the rendering of the element returned from `app.createRoot()`.
+We call `createApp` to create a new app instance, which is responsible for wiring together all of the features that we provide to the app. It also provides a set of built-in [Extensions](./20-extensions.md) that help build out the foundations of the app, as well as defaults for many other systems such as [Utility API](./33-utility-apis.md) implementations, components, icons, themes, and how to load configuration. No real work is done at the point of creating the app though, it's all deferred to the rendering of the element returned from `app.createRoot()`.
-It is possible to explicitly install features when creating the app, although typically these will instead be discovered automatically which we'll explore later on. Nevertheless these features are what build out the actual functionality of the app by providing [Extensions](./03-extensions.md). These extensions are wired together by the app into a tree structure known as the app extension tree. Each node in this tree receives data from its child nodes, and passes along data to its parent. The following diagram illustrates the shape of a small app extension tree.
+It is possible to explicitly install features when creating the app, although typically these will instead be discovered automatically which we'll explore later on. Nevertheless these features are what build out the actual functionality of the app by providing [Extensions](./20-extensions.md). These extensions are wired together by the app into a tree structure known as the app extension tree. Each node in this tree receives data from its child nodes, and passes along data to its parent. The following diagram illustrates the shape of a small app extension tree.

-Each node in this tree is an extension with a parent node and children. The colored shapes represent extension data inputs and output, where each color is one unique type of data. You can see that there are both extensions that output data that is ignored by the parent, as well as extensions that accept inputs but do not have any children. There are a couple of different tools at your disposal when creating and extension that lets you define different requirements for your inputs and output, which we will cover in greater details in the [Extensions](./03-extensions.md) section.
+Each node in this tree is an extension with a parent node and children. The colored shapes represent extension data inputs and output, where each color is one unique type of data. You can see that there are both extensions that output data that is ignored by the parent, as well as extensions that accept inputs but do not have any children. There are a couple of different tools at your disposal when creating and extension that lets you define different requirements for your inputs and output, which we will cover in greater details in the [Extensions](./20-extensions.md) section.
A common type of data that is shared between extensions is React elements and components. These can in turn be rendered by each other in their own React components, which ends up forming a parallel tree of React components that is similar in shape to that of the app extension tree. At the top of the app extension tree is a built-in root extension that among other things outputs a React element. This element also ends up being the root of the parallel React tree, and is rendered by the React element returned by `app.createRoot()`.
diff --git a/docs/frontend-system/architecture/15-plugins.md b/docs/frontend-system/architecture/15-plugins.md
new file mode 100644
index 0000000000..a54573427d
--- /dev/null
+++ b/docs/frontend-system/architecture/15-plugins.md
@@ -0,0 +1,91 @@
+---
+id: plugins
+title: Frontend Plugins
+sidebar_label: Plugins
+# prettier-ignore
+description: Frontend plugins
+---
+
+> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
+
+## Introduction
+
+Frontend plugins are a foundational building block in Backstage and the frontend system. They are used to encapsulate and provide functionality for a Backstage app, such as new pages, navigational elements, and APIs; as well as extensions and features for other plugins, such as entity page cards and content for the Software Catalog, or result list items for the search plugin.
+
+Each plugin is typically shipped in a separate NPM package, whether that's a published package, or just in the local workspace. The plugin instance should always the `default` export of the package, either via the main entry-point or the `/alpha` sub-path export. Each plugin package is limited to exporting a single plugin instance. In a local workspace you could use a different structure if preferred, but this is considered a non-standard layout and should be avoided in published packages.
+
+## Creating a Plugin
+
+Frontend plugin instances are created with the `createFrontendPlugin` function, which is provided by the `@backstage/frontend-plugin-api` package. It takes a single options object that provides all of the necessary configuration for the plugin. In particular you will want to provide [extensions](./20-extensions.md) for your plugin, as that is the way that you can provide new functionality to the app.
+
+```tsx
+// This creates a new extension, see "Extension Blueprints" documentation for more details
+const myPage = PageBlueprint.make({
+ params: {
+ defaultPath: '/my-page',
+ loader: () => import('./MyPage').then(m => ),
+ },
+});
+
+export default createFrontendPlugin({
+ id: 'my-plugin',
+ extensions: [myPage],
+});
+```
+
+### `pluginId` option
+
+Each plugin needs an ID, which is used to uniquely identify the plugin within an entire Backstage system. The ID does not have to be globally unique across all of the NPM ecosystem, although you generally want to strive for that. It is not possible to install multiple plugins with the same ID in a single Backstage app.
+
+The plugin ID should generally be part of the of the package name and use kebab-case. See both the [frontend naming patterns section](./50-naming-patterns.md), as well as the [package metadata section](../../tooling/package-metadata.md#name) for more information.
+
+### `extensions` option
+
+These are the [extensions](./20-extensions.md) that the plugin provides to the app. Note that you should not export any of these extensions separately from the plugin package, as they can already by accessed via the `getExtension` method of the plugin instance using the extension ID.
+
+The extensions that you provide to a plugin will have their `namespace` set to the plugin ID by default. For example, if you create an extension using the `PageBlueprint` without any particular naming options and install that via a plugin with the ID `my-plugin`, the final extension ID will be `page:my-plugin`. You can read more about how this works in the [extension structure documentation](./20-extensions.md#extension-structure).
+
+### `routes` and `externalRoutes` options
+
+These are the routes that the plugin exposes to the app. The `routes` option declares all of the target routes that your plugin provides, i.e. routes that other plugins link to. The `externalRoutes` option instead declares all the outgoing routes, i.e. routes that your plugins links to, which you can bind to the `routes` of other plugins. See the [routes documentation](./36-routes.md) for more information how to set up cross-plugin navigation.
+
+### `featureFlags` option
+
+This is a list of feature flag declarations that your plugin provides to the app. This makes sure that the feature flags are correctly registered and can be toggled in the app. To read a feature flag you can use the feature flags [Utility API](../architecture/33-utility-apis.md), accessible via `featureFlagsApiRef`.
+
+## Installing a Plugin in an App
+
+A plugin instance is considered a frontend feature and can be installed directly in any Backstage frontend app. See the [app documentation](./10-app.md) for more information about the different ways in which you can install new features in an app.
+
+## Overriding a Plugin
+
+A plugin might not always behave exactly the way you want. It could be that you want to remove particular extensions, decorate them a bit, replace them with your own, or simply add new ones. Regardless of your exact use-case, you can use the `plugin.withOverrides` method to create a new copy of the plugin with the desired changes. When doing so you can also access the original extensions provided by the plugin, and use the [extension override](./25-extension-overrides.md) API to make changes to individual extensions:
+
+```tsx
+import plugin from '@backstage/plugin-catalog';
+
+export default plugin.withOverrides({
+ // These overrides are merged with the original extensions
+ extensions: [
+ // Override the catalog nav item to use a custom icon
+ plugin.getExtension('nav-item:catalog').override({
+ factory: origFactory => [
+ NavItemBlueprint.dataRefs.target({
+ ...origFactory().get(NavItemBlueprint.dataRefs.target),
+ icon: CustomCatalogIcon,
+ }),
+ ],
+ }),
+ // Override the catalog index page with a completely custom implementation
+ PageBlueprint.make({
+ params: {
+ defaultPath: '/catalog',
+ routeRef: plugin.routes.catalogIndex,
+ loader: () => import('./CustomCatalogIndexPage').then(m => ),
+ },
+ }),
+ ],
+});
+```
+
+You can keep the plugin override in your app package, but it can often be a good idea to separate it out into its own package, especially if the overrides are complex or you want distinct ownership of the override. For example, if you are overriding the `@backstage/plugin-catalog` plugin, you might create a new package called `@internal/plugin-catalog` at `plugins/catalog` in your workspace, which exports the overridden plugin instance.
diff --git a/docs/frontend-system/architecture/03-extensions.md b/docs/frontend-system/architecture/20-extensions.md
similarity index 65%
rename from docs/frontend-system/architecture/03-extensions.md
rename to docs/frontend-system/architecture/20-extensions.md
index cdc73a17ec..02f4ba9d05 100644
--- a/docs/frontend-system/architecture/03-extensions.md
+++ b/docs/frontend-system/architecture/20-extensions.md
@@ -8,7 +8,7 @@ description: Frontend extensions
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
-As mentioned in the [previous section](./02-app.md), Backstage apps are built up from a tree of extensions. This section will go into more detail about what extensions are, how to create and use them, and how to create your own extensibility patterns.
+As mentioned in the [previous section](./10-app.md), Backstage apps are built up from a tree of extensions. This section will go into more detail about what extensions are, how to create and use them, and how to create your own extensibility patterns.
## Extension Structure
@@ -18,12 +18,12 @@ Each extensions has a number of different properties that define how it behaves
### ID
-
-
The ID of an extension is used to uniquely identity it, and it should ideally be unique across the entire Backstage ecosystem. For each frontend app instance there can only be a single extension for any given ID. Installing multiple extensions with the same ID will either result in an error or one of the extensions will override the others. The ID is also used to reference the extensions from other extensions, in configuration, and in other places such as developer tools and analytics.
+When creating an extension you do not provide the ID directly. Instead, you indirectly or directly provide the kind, namespace, and name parts that make up the ID. The kind is always provided by the [extension blueprint](./23-extension-blueprints.md), the only exception is if you use [`createExtension`](#creating-an-extensions) directly. Any extension that is provided by a plugin will by default have its namespace set to the plugin ID, so you generally only need to provide an explicit namespace if you want to override an existing extension. The name is also optional, and primarily used to distinguish between multiple extensions of the same kind and namespace. If a plugin doesn't need to distinguish between different extensions of the same kind, the name can be omitted.
+
+The extension ID will be constructed using the pattern `[:][][/][]`, where the separating `/` is only present if both a namespace and name are defined.
+
### Output
The output of an extension is the data that it provides to its parent extension, and ultimately its contribution to the app. The output itself comes in the form of a collection of arbitrary values, anything that can be represented as a TypeScript type. However, each individual output value must be associated with a shared reference known as an extension data reference. You must also use these same references to be able to access individual output values of an extension.
@@ -32,7 +32,7 @@ The output of an extension is the data that it provides to its parent extension,
The inputs of an extension define the data that it received from its children. Each extension can have multiple different inputs identified by an input name. These inputs each have their own set of data that they expect, which is defined as a collection of extension data references. An extension will only have access to the data that it has explicitly requested from each input.
-### Attachment Point
+### Attachment point
The attachment point of an extension decides where in the app extension tree it will be located. It is defined by the ID of the parent extension, as well as the name of the input to attach to. Through the attachment point the extension will share its own output as inputs to the parent extension. An extension can only be attached to an input that matches its own output, it is an error to try to attach an extension to an input the requires data that the extension does not provide in its output.
@@ -44,7 +44,7 @@ Each extension in the app can be disabled, meaning it will not be instantiated a
The ordering of extensions is sometimes very important, as it may for example affect in which order they show up in the UI. When an extension is toggled from disabled to enabled through configuration it resets the ordering of the extension, pushing it to the end of the list. It is generally recommended to leave extensions as disabled by default if their order is important, allowing for the order in which their are enabled in the configuration to determine their order in the app.
-### Configuration & Configuration Schema
+### Configuration & configuration schema
Each extension can define a configuration schema that describes the configuration that it accepts. This schema is used to validate the configuration provided by integrators, but also to fill in default configuration values. The configuration itself is provided by integrators in order to customize the extension. It is not possible to provide a default configuration of an extension, this must instead be done through defaults in the configuration schema. This allows for a simpler configuration logic where multiple configurations of the same extension completely replace each other rather than being merged.
@@ -54,7 +54,7 @@ The extension factory is the implementation of the extension itself. It is a fun
Extension factories should be lean and not do any heavy lifting or async work, as they are called during the initialization of the app. For example, if you need to do an expensive computation to generate your output, then prefer outputting a callback that does the computation instead. This allows the parent extension to defer the computation for later so that you avoid blocking the app startup.
-## Creating an Extensions
+## Creating an extension
Extensions are created using the `createExtension` function from `@backstage/frontend-plugin-api`. At minimum you need to provide an ID, attachment point, output definition, and a factory function. The following example shows the creation of a minimal extension:
@@ -64,34 +64,30 @@ const extension = createExtension({
// This is the attachment point, `id` is the ID of the parent extension,
// while `input` is the name of the input to attach to.
attachTo: { id: 'my-parent', input: 'content' },
- // The output map defines the outputs of the extension. The object keys
- // are only used internally to map the outputs of the factory and do
- // not need to match the keys of the input.
- output: {
- element: coreExtensionData.reactElement,
- },
+ // The output option defines the allowed and required outputs of the extension factory.
+ output: [coreExtensionData.reactElement],
// This factory is called to instantiate the extensions and produce its output.
factory() {
- return {
- element:
Hello World
,
- };
+ return [coreExtensionData.reactElement(
Hello World
)];
},
});
```
-Note that while the `createExtension` is public API and used in many places, it is not typically what you use when building plugins and features. Instead there are many extension creator functions exported by both the core APIs and plugins that make it easier to create extensions for more specific usages.
+Note that while the `createExtension` function is public API and used in many places, it is not typically what you use when building plugins and features. Instead there are many [extension blueprints](./23-extension-blueprints.md) exported by both the core APIs and plugins that make it easier to create extensions for more specific usages.
-## Extension Data
+## Extension data
Communication between extensions happens in one direction, from one child extension through the attachment point to its parent. The child extension outputs data which is then passed as inputs to the parent extension. This data is called Extension Data, where the shape of each individual piece of data is described by an Extension Data Reference. These references are created separately from the extensions themselves, and can be shared across multiple different kinds of extensions. Each reference consists of an ID and a TypeScript type that the data needs to conform to, and represents one type of data that can be shared between extensions.
-### Extension Data References
+### Extension data references
To create a new extension data reference to represent a type of shared extension data you use the `createExtensionDataRef` function. When defining a new reference you need to provide an ID and a TypeScript type, for example:
```ts
export const reactElementExtensionDataRef =
- createExtensionDataRef('my-plugin.reactElement');
+ createExtensionDataRef().with({
+ id: 'my-plugin.reactElement',
+ });
```
The `ExtensionDataRef` can then be used to describe an output property of the extension. This will enforce typing on the return value of the extension factory:
@@ -99,59 +95,53 @@ The `ExtensionDataRef` can then be used to describe an output property of the ex
```tsx
const extension = createExtension({
// ...
- output: {
- element: reactElementExtensionDataRef,
- },
+ output: [reactElementExtensionDataRef],
factory() {
- return {
- element:
Hello World
,
- };
+ return [reactElementExtensionDataRef(
Hello World
)];
},
});
```
-### Extension Data Uniqueness
+### Extension data uniqueness
-Note that the key used in the output map, in this case `element`, is only used internally within the definition of the extension itself. That actual identifier for the data when consumed by other extensions is the ID of the reference, in this case [`core.reactElement`](https://github.com/backstage/backstage/blob/916da47e8abdb880877daa18881eb8fdbb33e70a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts#L23). This means that you can not output multiple different values for the same extension data reference, as they would conflict with each other. That in turn makes overly generic extension data references a bad idea, for example a generic "string" type. Instead create separate references for each type of data that you want to share.
+Note that you are **not** allowed to repeat the same data reference in the outputs, or return multiple values for the same reference. Multiple outputs for the same reference will conflict with each other and cause an error. If you want to output multiple values of the same TypeScript type you should create separate references for each value. That in turn means that overly generic extension data references are a bad idea, for example a generic "string" type. Instead create separate references for each type of data that you want to share.
```tsx
const extension = createExtension({
// ...
- output: {
- // ❌ Bad example - outputting values of same type
- element1: reactElementExtensionDataRef,
- element2: reactElementExtensionDataRef,
- },
+ output: [
+ // ❌ Bad example - duplicate output declaration
+ reactElementExtensionDataRef,
+ reactElementExtensionDataRef,
+ ],
factory() {
- return {
- element1:
Hello World
,
- element2:
Hello World
,
- };
+ return [
+ // ❌ Bad example - duplicate output values
+ reactElementExtensionDataRef(
Hello
),
+ reactElementExtensionDataRef(
World
),
+ ];
},
});
```
-### Core Extension Data
+### Core extension data
We provide default `coreExtensionData`, which provides commonly used `ExtensionDataRef`s - e.g. for `React.JSX.Element` and `RouteRef`. They can be used when creating your own extension. For example, the React Element extension data that we defined above is already provided as `coreExtensionData.reactElement`.
For a full list and explanations of all types of core extension data, see the [core extension data reference](../building-plugins/04-built-in-data-refs.md).
-### Optional Extension Data
+### Optional extension data
By default all extension data is required, meaning that the extension factory must provide a value for each output. However, it is possible to make extension data optional by calling the `.optional()` method. This makes it optional for the factory function to return a value as part of its output. When calling the `.optional()` method you create a new copy of the extension data reference, it does not mutate the existing reference.
```tsx
const extension = createExtension({
// ...
- output: {
- element: coreExtensionData.reactElement.optional(),
- },
+ output: [coreExtensionData.reactElement.optional()],
factory() {
- return {
- element:
- Math.random() < 0.5 ? : undefined,
- };
+ return Math.random() > 0.5
+ ? [coreExtensionData.reactElement(
Hello World
)]
+ : [];
},
});
```
@@ -165,17 +155,19 @@ const navigationExtension = createExtension({
// ...
inputs: {
// [1]: Input
- logo: createExtensionInput(
- {
- element: coreExtensionData.reactElement,
- },
- { singleton: true, optional: true },
- ),
+ logo: createExtensionInput([coreExtensionData.reactElement], {
+ singleton: true,
+ optional: true,
+ }),
},
factory({ inputs }) {
return {
element: (
-
+
),
};
},
@@ -191,10 +183,9 @@ So how can we now attach the output to the parent extension's input? If we think
const navigationItemExtension = createExtension({
// ...
attachTo: { id: 'app/nav', input: 'items' },
+ output: [coreExtensionData.reactElement],
factory() {
- return {
- element: Home,
- };
+ return [coreExtensionData.reactElement(Home)];
},
});
@@ -203,26 +194,22 @@ const navigationExtension = createExtension({
// [2]: Extension `id` will be `app/nav` following the extension naming pattern
namespace: 'app',
name: 'nav',
- output: {
- element: coreExtensionData.reactElement,
- },
+ output: [coreExtensionData.reactElement],
inputs: {
- items: createExtensionInput({
- element: coreExtensionData.reactElement,
- }),
+ items: createExtensionInput([coreExtensionData.reactElement]),
},
factory({ inputs }) {
- return {
- element: (
+ return [
+ coreExtensionData.reactElement(
+ ,
),
- };
+ ];
},
// ...
});
@@ -230,27 +217,27 @@ const navigationExtension = createExtension({
In this case the extension input `items` is an array, where each individual item is an extension that attached itself to the extension inputs of this `id`.
-With the `inputs` not only the `output` of an extensions item is passed to the extension, but also the `node`. However, it is discouraged to consume the `node` here unless needed. If we are looking at the `factory` function from the example above we could access the `node` like the following:
+In addition to being able to access data passed through the input, you also have access to the underlying app `node`. This can be useful if you for example want to get the ID of the attached extension. However, avoid using the `node` unless needed, it is generally better to stick to only consuming the provided data. If we are looking at the `factory` function from the example above we could access the `node` like the following:
```tsx
// ...
factory({ inputs }) {
- return {
- element: (
+ return [
+ coreExtensionData.reactElement(
),
- };
+ ];
},
```
-## Extension Configuration
+## Extension configuration
With the `app-config.yaml` there is already the option to pass configuration to plugins or the app to e.g. define the `baseURL` of your app. For extensions this concept would be limiting as an extension can be independent of the plugin & initiated several times. Therefore we created a possibility to configure each extension individually through config. The extension config schema is created using the [`zod`](https://zod.dev/) library, which in addition to TypeScript type checking also provides runtime validation and coercion. If we continue with the example of the `navigationExtension` and now want it to contain a configurable title, we could make it available like the following:
@@ -260,20 +247,20 @@ const navigationExtension = createExtension({
namespace: 'app',
name: 'nav',
// [3]: Extension `id` will be `app/nav` following the extension naming pattern
- configSchema: createSchemaFromZod(z =>
- z.object({
- title: z.string().default('Sidebar Title'),
- }),
- ),
+ config: {
+ schema: {
+ title: z => z.string().default('Sidebar Title'),
+ },
+ },
factory({ config }) {
- return {
- element: (
+ return [
+ coreExtensionData.reactElement(
+ ,
),
- };
+ ];
},
// ...
});
@@ -291,47 +278,40 @@ app:
title: 'Backstage'
```
-## Extension Creators
+## Extension factory as a generator function
-With creating an extension by using `createExtension(...)` you have the advantage that the extension can be anything in your Backstage application. We realised that this comes with the trade-off of having to repeat boilerplate code for similar building blocks. Here extension creators come into play for covering common building blocks in Backstage like pages using `createPageExtension`, themes using the `createThemeExtension` or items for the navigation using `createNavItemExtension`.
+In all examples so far we have defined the extension factory as a regular function that returns its output in an array. However, the only requirement is that the factory function returns any iterable of extension data values. This means that you can also define the factory function as a generator function, which allows you to yield values one by one. This is particularly useful if you want to conditionally output values.
-If we follow the example from above all items of the navigation have similarities, like they all want to be attached to the same extension with the same input as well as rendering the same navigation item component. Therefore `createExtension` can be abstracted for this use case to `createNavItemExtension` and if we add the extension to the app it will end up in the right place & looks like we expect a navigation item to look.
+For example, this is how we could define an extension where its output depends on the configuration:
```tsx
-export const HomeNavIcon = createNavItemExtension({
- routeRef: routeRefForTheHomePage,
- title: 'Home',
- icon: HomeIcon,
+const exampleExtension = createExtension({
+ // ...
+ config: {
+ schema: {
+ disableIcon: z.boolean().default(false),
+ },
+ },
+ output: [coreExtensionData.reactElement, iconDataRef.optional()],
+ *factory({ config }) {
+ yield coreExtensionData.reactElement(
Hello World
);
+
+ if (!config.disableIcon) {
+ yield iconDataRef();
+ }
+ },
});
```
-### Extension Kind
-
-With the example `HomeNavIcon` will end up on the extension input `items` of the extensions with the id `app/nav`. It raises the question what the `id` of the `HomeNavIcon` itself is. The extension creator for the navigation item has a defined `kind`, which by convention matches the own name. So in this example `createNavItemExtension` sets the kind to `nav-item`.
-
-The `id` of the extension is then build out of `namespace`, `name` & `kind` like the following - where `namespace` & `name` are optional properties that can be passed to the extension creator:
-
-```
-id: kind:namespace/name
-```
-
-For more information on naming of extension refer to the [naming patterns documentation](./08-naming-patterns.md).
-
-### Extension Creators in libraries
-
-Extension creators should be exported from frontend library packages (e.g. `*-react`) rather than plugin packages.
-
-If an extension is only for in-house tweaks, it's okay to put it in the plugin package. But if you want other open source plugins to use it, or you already have a `-react` package, always put extension creators in the `-react` package.
-
-## Extension Boundary
+## Extension boundary
The `ExtensionBoundary` wraps extensions with several React contexts for different purposes
### Suspense
-All React elements rendered by extension creators should be wrapped in the extension boundary. With `Suspense` the extension can than load resources asynchronously with having a loading fallback. It also allows to lazy load the whole extension similar to how plugins are currently lazy loaded in Backstage.
+Most React elements rendered by extensions should be wrapped in the extension boundary. With `Suspense` the extension can than load resources asynchronously with having a loading fallback. It also allows to lazy load the whole extension similar to how plugins are currently lazy loaded in Backstage.
-### Error Boundary
+### Error boundary
Similar to plugins the `ErrorBoundary` for extension allows to pass in a fallback component in case there is an uncaught error inside of the component. With this the error can be isolated & it would prevent the rest of the plugin to crash.
@@ -339,32 +319,21 @@ Similar to plugins the `ErrorBoundary` for extension allows to pass in a fallbac
Analytics information are provided through the `AnalyticsContext`, which will give `extensionId` & `pluginId` as context to analytics event fired inside of the extension. Additionally `RouteTracker` will capture an analytics event for routable extension to inform which extension metadata gets associated with a navigation event when the route navigated to is a gathered `mountPoint`. Whether an extension is routable is inferred from its outputs, but you can also explicitly control this behavior by passing the `routable` prop to `ExtensionBoundary`.
-The `ExtensionBoundary` can be used like the following in an extension creator:
+The `ExtensionBoundary` can be used like the following in an extension:
```tsx
-export function createSomeExtension<
- TConfig extends {},
- TInputs extends AnyExtensionInputMap,
->(options): ExtensionDefinition {
- return createExtension({
- // ...
- factory({ config, inputs, node }) {
- const ExtensionComponent = lazy(() =>
- options
- .loader({ config, inputs })
- .then(element => ({ default: () => element })),
- );
-
- return {
- path: config.path,
- routeRef: options.routeRef,
- element: (
-
-
-
- ),
- };
- },
- });
-}
+const routableExtension = createExtension({
+ // ...
+ factory({ config, inputs, node }) {
+ return [
+ coreExtensionData.path(config.path),
+ coreExtensionData.routeRef(myRouteRef),
+ coreExtensionData.reactElement(
+
+
+ ,
+ ),
+ ];
+ },
+});
```
diff --git a/docs/frontend-system/architecture/23-extension-blueprints.md b/docs/frontend-system/architecture/23-extension-blueprints.md
new file mode 100644
index 0000000000..ee09e7fec6
--- /dev/null
+++ b/docs/frontend-system/architecture/23-extension-blueprints.md
@@ -0,0 +1,142 @@
+---
+id: extension-blueprints
+title: Frontend Extension Blueprints
+sidebar_label: Extensions Blueprints
+# prettier-ignore
+description: Frontend extensions
+---
+
+> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
+
+The `createExtension` function and related APIs is considered a low-level building and fairly advanced building block, and is not typically what you would use when building plugins and features. Instead, the core APIs and plugins provide extension blueprints that makes it easier to create extensions for specific usages. These blueprints accept a number of parameters that is up to each blueprint to define, and then creates a new extension using the provided parameters. New blueprints are created using the `createExtensionBlueprint` function, and are by convention exported with the symbol `Blueprint`. If you are curious about what blueprints are available from a plugin or package, look for `*Blueprint` exports in the package's API, for plugins these are typically found in the `*-react` package.
+
+## Creating an extension from a blueprint
+
+Every extension blueprint provides a `make` method that can be used to create new extensions. It is a simple way to create a new extension where the base blueprint provides all the necessary functionality. All you need to do is to provide the necessary blueprint parameters, but you also have the ability to provide additional options, for example a `name` for the extension.
+
+The following is a simple example of how one might use the blueprint `make` method to create a new extension:
+
+```tsx
+const myPageExtension = PageBlueprint.make({
+ params: {
+ defaultPath: '/my-page',
+ loader: () => import('./components/MyPage').them(m => ),
+ },
+});
+```
+
+The returned `myPageExtension` is an extension which is ready to be used in a plugin. It is the same type of object as is returned by the lower level `createExtension` function.
+
+## Creating an extension from a blueprint with overrides
+
+Every extension blueprint also provides a `makeWithOverrides` method. It is useful in cases where you want to provide additional integration points for an extension created with a blueprint. You might for example want to define additional inputs or configuration schema, or use the existing configuration to dynamically compute the parameters passed to the blueprint.
+
+The following is an example of how one might use the blueprint `makeWithOverrides` method to create a new extension:
+
+```tsx
+const myPageExtension = PageBlueprint.makeWithOverrides({
+ config: {
+ schema: {
+ layout: z => z.enum(['grid', 'rows']).default('grid'),
+ },
+ },
+ // The original blueprint factory is provided as the first argument
+ factory(originalFactory, { config }) {
+ // Call and forward the result from the original factory, providing
+ // the blueprint parameters as the first argument.
+ return originalFactory({
+ defaultPath: '/my-page',
+ loader: () =>
+ import('./components/MyPage').them(m => (
+ // We can now access values from the factory context when providing
+ // the blueprint parameters, such as config values.
+
+ )),
+ });
+ },
+});
+```
+
+When using `makeWithOverrides`, we no longer pass the blueprint parameters directly. Instead, we provide a `factory` function that receives the original blueprint factory as the first argument, and the extension factory context as the second. We can then call the original blueprint factory with the blueprint parameters and forward the result as the return value of out factory. Notice that when passing the blueprint parameters using this pattern we have access to a lot more information than when using the `make` method, at the cost of being more complex.
+
+Apart from the addition of the blueprint parameters of the first argument to the original factory function, the `makeWithOverrides` method works the same way as [extension overrides](./25-extension-overrides.md). All the same options and rules apply, including the ability to define additional inputs, override outputs, and so on. We therefore defer to the [extension overrides](./25-extension-overrides.md) documentation for more information on how to use the `makeWithOverrides` method.
+
+## Creating an extension blueprint
+
+To create a new extension blueprint, you use the `createExtensionBlueprint` function. At the surface it is very similar to `createExtension`, but with a few key differences. Firstly you must provide a `kind` option, which will be the kind of all extensions created with the blueprint. See the [naming patterns section](./50-naming-patterns.md) for more information about how to select a good extension kind. Secondly, the `factory` function has a new signature where the first parameter is the blueprint parameters, and the second is the factory context. And finally, rather than returning an extension, `createExtensionBlueprint` returns a blueprint object with the `make` method and friends, which is used as is described above.
+
+The following is an example of how one might create a new extension blueprint:
+
+```tsx
+export interface MyWidgetBlueprintParams {
+ defaultTitle: string;
+ element: JSX.Element;
+}
+
+export const MyWidgetBlueprint = createExtensionBlueprint({
+ kind: 'my-widget',
+ attachTo: { id: 'page:my-plugin', input: 'widgets' },
+ config: {
+ schema: {
+ title: z.string().optional(),
+ },
+ },
+ output: [coreExtensionData.reactElement],
+ factory(params: MyWidgetBlueprintParams, { config }) {
+ return [
+ // Note that while this is a valid pattern, you might often want to
+ // return separate pieces of data instead, more on that below.
+ coreExtensionData.reactElement(
+
+ {params.element}
+ ,
+ ),
+ ];
+ },
+});
+```
+
+This is of course a quite bare-bones example blueprint, but still a very real example. Blueprints can be very simple, there's already a lot of value in encapsulating the extension kind, attachment point, and output in a blueprint.
+
+Most of the options provided to `createExtensionBlueprint` can be overridden when using `makeWithOverrides` to create an extension from the blueprint. These overrides work the same way as [extension overrides](./25-extension-overrides.md), and we defer to that documentation for more information on how overrides work.
+
+### Blueprint-specific extension data references
+
+In some cases you may want to define and provide [extension data reference](./20-extensions.md#extension-data-references) that are specific to your blueprint. In the above example we might want to forward the `title` as data for example, rather than encapsulating it into the `MyWidgetContainer` component. This gives the parent extension more flexibility in the rendering for our example widget extensions.
+
+To do that, we create a new extension data reference for our widget title. This references is provided via the `dataRefs` options when we create the blueprint, which makes it available for use via `MyWidgetBlueprint.dataRefs.widgetTitle`.
+
+```tsx
+export interface MyWidgetBlueprintParams {
+ defaultTitle: string;
+ element: JSX.Element;
+}
+
+const widgetTitleRef = createExtensionDataRef().with({
+ id: 'my-plugin.widget.title',
+});
+
+export const MyWidgetBlueprint = createExtensionBlueprint({
+ kind: 'my-widget',
+ attachTo: { id: 'page:my-plugin', input: 'widgets' },
+ config: {
+ schema: {
+ title: z.string().optional(),
+ },
+ },
+ output: [widgetTitleRef, coreExtensionData.reactElement],
+ factory(params: MyWidgetBlueprintParams, { config }) {
+ return [
+ widgetTitleRef(config.title ?? params.defaultTitle),
+ coreExtensionData.reactElement(params.element),
+ ];
+ },
+ dataRefs: {
+ widgetTitle: widgetTitleRef,
+ },
+});
+```
+
+### Extension Blueprints in libraries
+
+If you are publishing a plugin, the extension creators should always be exported from frontend library packages (e.g. `*-react`) rather than plugin packages.
diff --git a/docs/frontend-system/architecture/25-extension-overrides.md b/docs/frontend-system/architecture/25-extension-overrides.md
new file mode 100644
index 0000000000..d47e2697a9
--- /dev/null
+++ b/docs/frontend-system/architecture/25-extension-overrides.md
@@ -0,0 +1,307 @@
+---
+id: extension-overrides
+title: Frontend Extension Overrides
+sidebar_label: Extension Overrides
+# prettier-ignore
+description: Frontend extension overrides
+---
+
+> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
+
+## Introduction
+
+An important customization point in the frontend system is the ability to override existing extensions. It can be used for anything from slight tweaks to the extension logic, to completely replacing an extension with a custom implementation. While extensions are encouraged to make themselves configurable, there are many situations where you need to override an extension to achieve the desired behavior. The ability to override extensions should be kept in mind when building plugins, and can be a powerful tool to allow for deeper customizations without the need to re-implement large parts of the plugin.
+
+In general, most features should have a good level of customization built into them, so that users do not have to leverage extension overrides to achieve common goals. A well written feature often has [configuration](../../conf/) settings, or uses extension inputs for extensibility where applicable. An example of this is the search plugin, which allows you to provide result renderers as inputs rather than replacing the result page wholesale just to tweak how results are shown. Adopters should take advantage of those when possible in order to reduce the need and size of extension overrides.
+
+## Overriding an extension
+
+Every extension created with `createExtension` comes with an `override` method, including those created from an [extension blueprint](./23-extension-blueprints.md). The `override` method **creates a new extension**, it does not mutate the existing extension. This new extension in created in such a way that if it is installed adjacent to the existing extension, it will take precedence and override the existing extension. While the `override` method does create new extension instances, it is not intended to be used as a way to create multiple new extensions from a base template, for that use-case you will want to use an [extension blueprint](./23-extension-blueprints.md) instead.
+
+The following is an example of calling the `.override(...)` method on an extension:
+
+```tsx
+const myOverrideExtension = myExtension.override({
+ factory(originalFactory) {
+ return originalFactory();
+ },
+});
+```
+
+This override is a no-op, it does not change the behavior of the extension, but simply forwards the outputs from the original extension factory. If you are familiar with [extension blueprints](./23-extension-blueprints.md), you will recognize this factory override pattern where we get access to the original factory function. In fact the only difference is that we do not need to pass any parameters to the original factory. The first parameter is now instead the optional factory context overrides, more on that as we dive into each override pattern in the following sections.
+
+## Overriding original factory outputs
+
+When overriding an extension you can choose to forward the existing outputs, or replace them with your own. The override factory has an exception to the rule that extension factories can only return a single value for each declared output. It will instead always use the **last** value provided for each extension data reference. This makes it possible to forward the outputs from the original factory, but also provide your own, for example:
+
+```tsx
+const myOverrideExtension = myExtension.override({
+ factory(originalFactory) {
+ return [
+ ...originalFactory(),
+ coreExtensionData.reactElement(
Hello Override
),
+ ];
+ },
+});
+```
+
+You can also access individual data values from the original factory, in order to decorate the output:
+
+```tsx
+const myOverrideExtension = myExtension.override({
+ factory(originalFactory) {
+ const originalOutput = originalFactory();
+ const originalElement = originalOutput.get(coreExtensionData.reactElement);
+
+ return [
+ ...originalOutput,
+ coreExtensionData.reactElement(
+
+ Show original element
+ {originalElement}
+ ,
+ ),
+ ];
+ },
+});
+```
+
+Just as [extension factories can be declared as a generator function](./20-extensions.md#extension-factory-as-a-generator-function), so can the override factory. Using a generator function, the first example above can be written as follows:
+
+```tsx
+const myOverrideExtension = myExtension.override({
+ *factory(originalFactory) {
+ yield* originalFactory();
+ yield coreExtensionData.reactElement(
Hello Override
);
+ },
+});
+```
+
+Note the `yield*` expression, which forwards all values from the provided iterable to the generator, in this case the original factory output.
+
+## Overriding declared outputs
+
+When overriding an extension you can provide a new output declaration. This **replaces** any existing output declaration, which means that if you want to forward any of the original output you will need to declare it again. The following example shows how to override an extension and replace the output declaration:
+
+```tsx
+// Original extension
+const exampleExtension = createExtension({
+ name: 'example',
+ output: [coreExtensionData.reactElement],
+ factory: () => [coreExtensionData.reactElement(
Example
)],
+});
+
+// Override extension, with additional outputs
+const overrideExtension = exampleExtension.override({
+ output: [coreExtensionData.reactElement, coreExtensionData.routePath],
+ factory(originalFactory) {
+ return [...originalFactory(), coreExtensionData.routePath('/example')];
+ },
+});
+```
+
+When overriding the output declaration you don't need to include the original outputs. Just remember that you will no longer be able to directly forward the output from the original factory, and will still need to adhere to the contract of the input that the extension is attached to.
+
+## Overriding declared inputs
+
+When overriding an extension you can also provide new input declarations. You can define any number of new inputs, but you are **not** able to override the existing inputs declared by the original extension. The new inputs will be merged with the existing ones, giving the override factory access to both. The following example shows how to override an extension and add a new input declaration:
+
+```tsx
+const myOverrideExtension = myExtension.override({
+ inputs: {
+ myOverrideInput: createExtensionInput([coreExtensionData.reactElement]),
+ },
+ factory(originalFactory, { inputs }) {
+ const originalOutput = originalFactory();
+ const originalElement = originalOutput.get(coreExtensionData.reactElement);
+
+ return [
+ ...originalOutput,
+ coreExtensionData.reactElement(
+
+
Original element
+ {originalElement}
+
Additional inputs
+
+ {inputs.myOverrideInput.map(i => (
+
+ {i.get(coreExtensionData.reactElement)}
+
+ ))}
+
+
,
+ ),
+ ];
+ },
+});
+```
+
+## Overriding configuration schema
+
+Overriding the configuration schema works very similarly to overriding the declared inputs. You can define new configuration fields that will be merged with the existing ones, but you can not re-declare existing fields. The following example shows how to override an extension and add a new configuration field:
+
+```tsx
+const exampleExtension = createExtension({
+ config: {
+ schema: {
+ foo: z => z.string(),
+ },
+ },
+ // ...
+});
+
+const overrideExtension = exampleExtension.override({
+ config: {
+ schema: {
+ bar: z => z.string(),
+ },
+ },
+ factory(originalFactory, { config }) {
+ //
+ console.log(`foo=${config.foo} bar=${config.bar}`);
+ return originalFactory();
+ },
+});
+```
+
+## Overriding original factory config context
+
+In all examples so far we have called the `originalFactory` callback without any arguments. It is however possible to override parts of the factory context for the original factory using the first parameter of the original factory. This can be useful if you want to override the provided configuration or change the inputs in some way. Note that if you are implementing a `factory` for a blueprint, the override factory context will instead be the second parameter of the original factory function. The following is an example of how to override the configuration for the original factory:
+
+```tsx
+const exampleExtension = createExtension({
+ name: 'example',
+ config: {
+ schema: {
+ layout: z => z.enum(['grid', 'list']).optional(),
+ },
+ },
+ output: [coreExtensionData.reactElement],
+ factory: ({ config }) => [
+ coreExtensionData.reactElement(
+ ,
+ ),
+ ],
+});
+
+const overrideExtension = exampleExtension.override({
+ factory(originalFactory, { config }) {
+ return originalFactory({
+ config: {
+ // Switch default layout from 'list' to 'grid'
+ layout: config.layout ?? 'grid',
+ },
+ });
+ },
+});
+```
+
+As can be seen in the above example we can provide a new configuration object in the `originalFactory` call using the `config` property. When providing the `config` property we will completely override the original configuration object that would otherwise have been provided to the original factory. Note that this object must adhere to the output type of the configuration schema, which might not be intuitive. It's due to the configuration having already been processed and validated by Zod at this point, which means that things like defaults in the schema will not be applied again.
+
+## Overriding original factory inputs context
+
+In addition to the configuration, you are also able to override the inputs provided to the original factory. Just like when overriding configuration you will completely replace the original inputs with the new ones, but you are able to forward the inputs that you are receiving to the override factory.
+
+You can override each input in one of two ways, which can not be combined. You can forward (or not forward) the original input, optionally filtering out individual items or reordering them. Or you can provide new values for the input, which will replace the original input. When providing new values you must forward all existing inputs and the inputs can not be reordered, and when forwarding the existing inputs you can not provide new values.
+
+The following example shows how to override the values provided for each input item:
+
+```tsx
+const exampleExtension = createExtension({
+ inputs: {
+ items: createExtensionInput([coreExtensionData.reactElement]),
+ },
+ // ...
+});
+
+const overrideExtension = exampleExtension.override({
+ factory(originalFactory, { inputs }) {
+ return originalFactory({
+ inputs: {
+ items: inputs.items.map(i => [
+ coreExtensionData.reactElement(
+ {i.get(coreExtensionData.reactElement)},
+ ),
+ ]),
+ },
+ });
+ },
+});
+```
+
+In contrast, the following example shows how to forward the original inputs, but in a different order:
+
+```tsx
+const exampleExtension = createExtension({
+ inputs: {
+ content: createExtensionInput([coreExtensionData.reactElement], {
+ singleton: true,
+ optional: true,
+ }),
+ items: createExtensionInput([coreExtensionData.reactElement]),
+ },
+ // ...
+});
+
+const overrideExtension = exampleExtension.override({
+ factory(originalFactory, { inputs }) {
+ return originalFactory({
+ inputs: {
+ // We can also skip forwarding the original input, if we want to remove it
+ content: inputs.content,
+ // Sort items input by their extension ID
+ items: inputs.items.toSorted((a, b) =>
+ a.node.spec.id.localeCompare(b.node.spec.id),
+ ),
+ },
+ });
+ },
+});
+```
+
+## Installing override extension in an app
+
+To install extension overrides in a Backstage app you should use `plugin.withOverrides` whenever you are overriding or adding extensions for a plugin. See the section on [overriding a plugin](./15-plugins.md#overriding-a-plugin) for more information.
+
+There is also a `createExtensionOverrides` function that can be used to install a collection of standalone extensions in an app. This method will be replaced with a different mechanism in the future, but for now remains the only way to override the built-in extensions in the app or to package extensions for a plugin package separate from the plugin itself.
+
+Note that while using either of these options you don't necessarily need to use the extension `.override(...)` method to create the overrides. You can also create new extensions with `createExtension` or a blueprint that are either completely net-new extensions, or override an existing extension by using the same `kind`, `namespace` and `name` to produce the same extension ID.
+
+### Creating a standalone extension bundle
+
+The following example shows how to create a standalone extension bundle that overrides the search page from the search plugin:
+
+```tsx
+import {
+ createPageExtension,
+ createExtensionOverrides,
+} from '@backstage/frontend-plugin-api';
+
+const customSearchPage = PageBlueprint.make({
+ // Since this is a standalone extension we need to provide the namespace to match the search plugin
+ namespace: 'search',
+ params: {
+ defaultPath: '/search',
+ loader: () =>
+ import('./CustomSearchPage').then(m => ),
+ },
+});
+
+export default createExtensionOverrides({
+ extensions: [customSearchPage],
+});
+```
+
+Assuming the above code resides in the `@internal/search-page` package, you can install it in your app like this:
+
+```tsx title="packages/app/src/App.tsx"
+import { createApp } from '@backstage/frontend-app-api';
+import searchPageOverride from '@internal/search-page';
+
+const app = createApp({
+ // highlight-next-line
+ features: [searchPageOverride],
+});
+
+export default app.createRoot();
+```
diff --git a/docs/frontend-system/architecture/08-references.md b/docs/frontend-system/architecture/30-references.md
similarity index 100%
rename from docs/frontend-system/architecture/08-references.md
rename to docs/frontend-system/architecture/30-references.md
diff --git a/docs/frontend-system/architecture/06-utility-apis.md b/docs/frontend-system/architecture/33-utility-apis.md
similarity index 97%
rename from docs/frontend-system/architecture/06-utility-apis.md
rename to docs/frontend-system/architecture/33-utility-apis.md
index 49e5854b65..c20936f819 100644
--- a/docs/frontend-system/architecture/06-utility-apis.md
+++ b/docs/frontend-system/architecture/33-utility-apis.md
@@ -10,7 +10,7 @@ description: Utility APIs
## Overview
-Utility APIs are pieces of standalone functionality, interfaces that can be requested by plugins to use. They are defined by a TypeScript interface as well as a reference (an "API ref") used to access its implementation. They can be provided both by plugins and the core framework, and are themselves [extensions](../architecture/03-extensions.md) that can have inputs, be replaced, and be declaratively configured in your app-config.
+Utility APIs are pieces of standalone functionality, interfaces that can be requested by plugins to use. They are defined by a TypeScript interface as well as a reference (an "API ref") used to access its implementation. They can be provided both by plugins and the core framework, and are themselves [extensions](../architecture/20-extensions.md) that can have inputs, be replaced, and be declaratively configured in your app-config.
A common example of a utility API is a client interface to interact with the backend part of a plugin, such as the catalog client. Any frontend plugin can then request an implementation of that interface to make requests through.
diff --git a/docs/frontend-system/architecture/07-routes.md b/docs/frontend-system/architecture/36-routes.md
similarity index 90%
rename from docs/frontend-system/architecture/07-routes.md
rename to docs/frontend-system/architecture/36-routes.md
index 9c6617a06f..e684932414 100644
--- a/docs/frontend-system/architecture/07-routes.md
+++ b/docs/frontend-system/architecture/36-routes.md
@@ -42,7 +42,7 @@ The code snippet in the previous section does not indicate which plugin the rout
```tsx title="plugins/catalog/src/plugin.tsx"
import React from 'react';
import {
- createPlugin,
+ createFrontendPlugin,
createPageExtension,
} from '@backstage/frontend-plugin-api';
import { indexRouteRef } from './routes';
@@ -55,7 +55,7 @@ const catalogIndexPage = createPageExtension({
loader: () => import('./components').then(m => ),
});
-export default createPlugin({
+export default createFrontendPlugin({
id: 'catalog',
// highlight-start
routes: {
@@ -101,23 +101,26 @@ export const IndexPage = () => {
return (
);
};
```
-We use the `useRouteRef` hook to create a link generator function that returns the details page path. We then call the link generator, passing it an object with the kind, namespace, and name. These parameters are used to construct a concrete path to the "Foo" details page.
+We use the `useRouteRef` hook to create a link generator function that returns the details page path. First we need to check whether the route is available, the link generator function will be `undefined` if it isn't. We then call the link generator, passing it an object with the kind, namespace, and name. These parameters are used to construct a concrete path to the "Foo" details page.
Let's see how the details page can get the parameters from the URL:
@@ -176,8 +179,11 @@ export const IndexPage = () => {
return (
);
};
@@ -190,7 +196,7 @@ Now the only thing left is to provide the page and external route via a plugin:
```tsx title="plugins/catalog/src/plugin.tsx"
import React from 'react';
import {
- createPlugin,
+ createFrontendPlugin,
createPageExtension,
useRouteRef,
} from '@backstage/frontend-plugin-api';
@@ -202,7 +208,7 @@ const catalogIndexPage = createPageExtension({
loader: () => import('./components').then(m => ),
});
-export default createPlugin({
+export default createFrontendPlugin({
id: 'catalog',
routes: {
index: indexRouteRef,
@@ -289,42 +295,6 @@ export const createComponentExternalRouteRef = createExternalRouteRef({
});
```
-### Optional External Route References
-
-It is possible to define an `ExternalRouteRef` as optional, so it is not required to bind it in the app.
-
-```tsx title="plugins/catalog/src/routes.ts"
-import { createExternalRouteRef } from '@backstage/frontend-plugin-api';
-
-export const createComponentExternalRouteRef = createExternalRouteRef({
- // highlight-next-line
- optional: true,
-});
-```
-
-When calling `useRouteRef` with an optional external route, its return signature is changed to `RouteFunc | undefined`, and the returned value can be used to decide whether a certain link should be displayed or if an action should be taken:
-
-```tsx title="plugins/catalog/src/components/IndexPage.tsx"
-import React from 'react';
-import { useRouteRef } from '@backstage/frontend-plugin-api';
-import { createComponentExternalRouteRef } from '../routes';
-
-export const IndexPage = () => {
- const getCreateComponentPath = useRouteRef(createComponentExternalRouteRef);
- return (
-
-
Index Page
- {/* Rendering the link only if the getCreateComponentPath is defined */}
- {/* highlight-start */}
- {getCreateComponentPath && (
- Create Component
- )}
- {/* highlight-end */}
-
- );
-};
-```
-
## Sub Route References
The last kind of route ref that can be created is a `SubRouteRef`, which can be used to create a route ref with a fixed path relative to an absolute `RouteRef`. They are useful if you have a page that internally is mounted at a sub route of a page extension component, and you want other plugins to be able to route to that page. And they can be a useful utility to handle routing within a plugin itself as well.
@@ -368,17 +338,21 @@ import { DetailsPage } from './DetailsPage';
export const IndexPage = () => {
const { pathname } = useLocation();
+
+ // highlight-start
const getIndexPath = useRouteRef(indexRouteRef);
const getDetailsPath = useRouteRef(detailsSubRouteRef);
+ // highlight-end
+
return (
@@ -429,7 +404,7 @@ Finally, see how a plugin can provide subroutes:
```tsx title="plugins/catalog/src/plugin.tsx"
import React from 'react';
import {
- createPlugin,
+ createFrontendPlugin,
createPageExtension,
} from '@backstage/frontend-plugin-api';
import { indexRouteRef, detailsSubRouteRef } from './routes';
@@ -440,7 +415,7 @@ const catalogIndexPage = createPageExtension({
loader: () => import('./components').then(m => ),
});
-export default createPlugin({
+export default createFrontendPlugin({
id: 'catalog',
routes: {
index: indexRouteRef,
diff --git a/docs/frontend-system/architecture/08-naming-patterns.md b/docs/frontend-system/architecture/50-naming-patterns.md
similarity index 87%
rename from docs/frontend-system/architecture/08-naming-patterns.md
rename to docs/frontend-system/architecture/50-naming-patterns.md
index a17525f3ce..11e0b8e733 100644
--- a/docs/frontend-system/architecture/08-naming-patterns.md
+++ b/docs/frontend-system/architecture/50-naming-patterns.md
@@ -23,7 +23,7 @@ Example:
```ts
// This declaration is only for internal usage in tests. This could also be a direct default export.
-export const userSettingsPlugin = createPlugin({
+export const userSettingsPlugin = createFrontendPlugin({
id: 'user-settings',
...
})
@@ -42,7 +42,7 @@ Note that while we use this naming pattern for the plugin instance this is only
| ID | `[:][/]` | `'core.nav'`, `'page:user-settings'`, `'entity-card:catalog/about'` |
| Symbol | `[][]` | `coreNav`, `userSettingsPage`, `catalogAboutEntityCard` |
-When you create a new extension you never provide the ID directly. Instead, you indirectly or directly provide the kind, namespace, and name parts that make up the ID. The kind is always provided by the blueprint creator, the only exception is if you use `createExtension` directly. Any extension that is provided by a plugin will by default have its namespace set to the plugin ID, so you generally only need to provide an explicit namespace if you want to override an existing extension. The name is also optional, and primarily used to distinguish between multiple extensions of the same kind and namespace. If a plugin doesn't need to distinguish between different extensions of the same kind, the name can be omitted.
+When you create a new extension you never provide the ID directly. Instead, you indirectly or directly provide the kind, namespace, and name parts that make up the ID. The kind is always provided by the extension blueprint, the only exception is if you use `createExtension` directly. Any extension that is provided by a plugin will by default have its namespace set to the plugin ID, so you generally only need to provide an explicit namespace if you want to override an existing extension. The name is also optional, and primarily used to distinguish between multiple extensions of the same kind and namespace. If a plugin doesn't need to distinguish between different extensions of the same kind, the name can be omitted.
Example:
@@ -67,7 +67,7 @@ const catalogSearchResultListItem = SearchResultListItemBlueprint.make({
});
// Note that the extensions themselves are not exported, only the plugin instance
-export const catalogPlugin = createPlugin({
+export const catalogPlugin = createFrontendPlugin({
id: 'catalog',
extensions: [catalogEntityPage, catalogSearchResultListItem /* ... */],
});
@@ -98,9 +98,9 @@ export interface SearchResultItemExtensionData {
}
export const searchResultItemExtensionDataRef =
- createExtensionDataRef(
- 'search.search-result-item',
- );
+ createExtensionDataRef().with({
+ id: 'search.search-result-item',
+ });
```
#### Grouped Extension Data
@@ -109,8 +109,12 @@ This way of defining extension data is similar to the standalone way, but it use
```ts
export const coreExtensionData = {
- reactElement: createExtensionDataRef('core.react-element'),
- routePath: createExtensionDataRef('core.route-path'),
+ reactElement: createExtensionDataRef().with({
+ id: 'core.react-element',
+ }),
+ routePath: createExtensionDataRef().with({
+ id: 'core.route-path',
+ }),
};
```
@@ -125,9 +129,9 @@ export function createGraphiQLEndpointExtension(options) {
// Use a TypeScript namespace to merge the extension data references with the extension creator
export namespace createGraphiQLEndpointExtension {
- export const endpointDataRef = createExtensionDataRef* ... */>(
- 'graphiql.graphiql-endpoint.endpoint',
- );
+ export const endpointDataRef = createExtensionDataRef* ... */>().with({
+ id: 'graphiql.graphiql-endpoint.endpoint',
+ });
}
```
diff --git a/docs/frontend-system/architecture/60-migrations.md b/docs/frontend-system/architecture/60-migrations.md
new file mode 100644
index 0000000000..a1ab0197f7
--- /dev/null
+++ b/docs/frontend-system/architecture/60-migrations.md
@@ -0,0 +1,104 @@
+---
+id: migrations
+title: Frontend System Migrations
+sidebar_label: Migrations
+# prettier-ignore
+description: Migration documentation for different versions of the frontend system core APIs.
+---
+
+> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
+
+## Introduction
+
+This section provides migration guides for different versions of the frontend system core APIs. Each guide will provide a summary of the changes that have been made for a particular Backstage release, and how to update your usage of the core APIs.
+
+This guide is intended for app and plugin authors who have already migrated their code to the new frontend system, and are looking to keep it up to date with the latest changes. These guides do not cover trivial migrations that can be explained in a deprecation message, such as a renamed export.
+
+## 1.30
+
+### Reworked extension inputs and outputs
+
+In previous versions of the frontend system you would define extension inputs and outputs as an "data map" where each named property corresponded to a piece of data that could be accessed via the same name in the extension factory. In order to better support extension overrides and blueprints, as well as reduce the risk of confusion, these data maps have been replaced by an array of data references.
+
+For example, an extension previously declared like this:
+
+```tsx
+createExtension({
+ name: 'example',
+ attachTo: { id: 'some-extension', input: 'content' },
+ inputs: {
+ header: createExtensionInput(
+ { element: coreExtensionData.reactElement },
+ {
+ optional: true,
+ singleton: true,
+ },
+ ),
+ },
+ output: { element: coreExtensionData.reactElement },
+ factory({ inputs }) {
+ return {
+ element: ,
+ };
+ },
+});
+```
+
+Would now look like this:
+
+```tsx
+createExtension({
+ name: 'example',
+ attachTo: { id: 'some-extension', input: 'content' },
+ inputs: {
+ header: createExtensionInput([coreExtensionData.reactElement], {
+ optional: true,
+ singleton: true,
+ }),
+ },
+ output: [coreExtensionData.reactElement],
+ factory({ inputs }) {
+ return [
+ coreExtensionData.reactElement(
+ ,
+ ),
+ ];
+ },
+});
+```
+
+Note the changes to the `inputs` and `output` declarations, as well as how these are used in the factory. Both the `inputs` and `output` now declare their expected data using an array, without names tied to each piece of data. The `get` method on the input object is used to access the input data, and accept a data reference matching the data that was declared for that input.
+
+The biggest change in practice is how the extension factories output their data. Instead of returning an object with the data values, you instead use each extension data reference to encapsulate a value and return a collection of these encapsulated values from the factory.
+
+### Blueprints instead of extension creators
+
+The "extension creator" pattern where `createExtension` was wrapped up in a function for creating specific kind of extensions has been replaced by [extension blueprints](./23-extension-blueprints.md). Extension creators were hard to implement and to maintain, with blueprints providing a much more consistent and powerful API surface for both plugin builders and integrators. For example, `createPageExtension` was the extension creator equivalent of `PageBlueprint`.
+
+Replace all existing usages of extension creators in your plugin or app with the corresponding blueprint. For a given extension creator `createExtension`, the blueprint equivalent is `Blueprint`. There might be slight differences in the API between the two, but most often you just need to move the kind-specific options of the extension creator to be passed as parameters to the blueprint. Note that if your extension declared any additional inputs or config you'll need to use the [`.makeWithOverrides`](./23-extension-blueprints.md#creating-an-extension-from-a-blueprint-with-overrides) method of the blueprint.
+
+If your plugin exports and extension creators, these should be migrated to blueprints instead.
+
+### Extension tester rework
+
+The `createExtensionTester` from the `@backstage/frontend-test-utils` package has been reworked to better support testing of extensions. The new API allows access to extension output directly using the new `.get(ref)` method, and also provides access to all tested extensions through the new `.query(id/extension)` method.
+
+The `.render()` method that used to render the test subject in a test app has been deprecated. The extension tester no longer constructs a full app tree, but instead only instantiates the tree for the extensions under test. If you want to test the rendering of an extension that outputs in an app, you can instead use the `renderInTestApp` utility in combination with the new `.reactElement()` method of the extension tester: `renderInTestApp(tester.reactElement())`.
+
+### Extension data references update
+
+The way that extension data references are declared has been changed to allow for type inference of the ID. This requires the declaration to be split into two separate function calls, one to supply the type and the other to infer any options. For example, a reference that was previously declared like this:
+
+```ts
+export const myExtension = createExtensionDataRef('my-plugin.my-data');
+```
+
+Should be updated to the following:
+
+```ts
+export const myExtension = createExtensionDataRef().with({
+ id: 'my-plugin.my-data',
+});
+```
diff --git a/docs/frontend-system/building-apps/01-index.md b/docs/frontend-system/building-apps/01-index.md
index ea7fe20948..16aacc1185 100644
--- a/docs/frontend-system/building-apps/01-index.md
+++ b/docs/frontend-system/building-apps/01-index.md
@@ -66,7 +66,7 @@ Linking routes from different plugins requires this configuration. You can do th
### Enable feature discovery
-Use this setting to enable experimental feature discovery when building your app with `@backstage/cli`. With this configuration your application tries to discover and install package extensions automatically, check [here](../architecture/02-app.md#feature-discovery) for more details.
+Use this setting to enable experimental feature discovery when building your app with `@backstage/cli`. With this configuration your application tries to discover and install package extensions automatically, check [here](../architecture/10-app.md#feature-discovery) for more details.
:::warning
Remember that package extensions that are not auto-discovered must be manually added to the application when creating an app. See [features](#install-features-manually) for more details.
@@ -78,7 +78,7 @@ It is possible to enable, disable and configure extensions individually in the `
### Customize or override built-in extensions
-Previously you would customize the application route, components, apis, sidebar, etc. through the code in `App.tsx`. Now we want you to write less code and install more extensions to customize your Backstage instance. See [here](../building-plugins/03-extension-types.md) which types of extensions are available for you to customize your application.
+Previously you would customize the application routes, components, apis, sidebar, etc. through the code in `App.tsx`. Now we want to allow the same thing to be achieved while writing less code and instead installing more extensions to customize your Backstage instance. See the [extension blueprints](../building-plugins/03-common-extension-blueprints.md) section for a list of common extension kinds that are available for you to customize and extend your application.
## Use code to customize the app at a more granular level
@@ -99,7 +99,7 @@ export default app.createRoot();
```
:::info
-You can also pass overrides to the features array, for more details, please read the [extension overrides](../architecture/05-extension-overrides.md) documentation.
+You can also pass overrides to the features array, for more details, please read the [extension overrides](../architecture/25-extension-overrides.md) documentation.
:::
### Using an async features loader
diff --git a/docs/frontend-system/building-apps/03-built-in-extensions.md b/docs/frontend-system/building-apps/03-built-in-extensions.md
index 4f8417f74f..5f74100df0 100644
--- a/docs/frontend-system/building-apps/03-built-in-extensions.md
+++ b/docs/frontend-system/building-apps/03-built-in-extensions.md
@@ -24,7 +24,7 @@ Be careful when disabling built-in extensions, as there may be other extensions
## Override built-in extensions
-You can override any built-in extension whenever their customizations, whether through configuration or input, do not meet a use case for your Backstage instance. Check out [this](../architecture/05-extension-overrides.md) documentation on how to override application extensions.
+You can override any built-in extension whenever their customizations, whether through configuration or input, do not meet a use case for your Backstage instance. Check out [this](../architecture/25-extension-overrides.md) documentation on how to override application extensions.
:::warning
Be aware there could be some implementation requirements to properly override an built-in extension, such as using same apis and do not remove inputs or configurations otherwise you can cause a side effect in other parts of the system that expects same minimal behavior.
@@ -45,6 +45,7 @@ This extension is the first extension attached to the extension tree. It is resp
| themes | The app themes list. | [createThemeExtension.themeDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createthemeextension.themedataref) | false | See [default themes](#default-theme-extensions). | [createThemeExtension](https://backstage.io/docs/reference/frontend-plugin-api.createthemeextension) |
| components | The app components list. | [createComponentExtension.componentDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createcomponentextension.componentdataref) | false | See [default components](#default-components-extensions). | [createComponentExtension](https://backstage.io/docs/reference/frontend-plugin-api.createcomponentextension) |
| translations | The app translations list. | [createTranslationExtension.translationDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createtranslationextension.translationdataref) | false | - | [createTranslationExtension](https://backstage.io/docs/reference/frontend-plugin-api.createtranslationextension) |
+| icons | The app icons list. | [IconBundleBlueprint.dataRefs.icons](https://backstage.io/docs/reference/frontend-plugin-api.iconbundleblueprint.dataRefs.icons) | true | - | [IconBundleBlueprint](https://backstage.io/docs/reference/frontend-plugin-api.iconbundleblueprint) |
#### Default theme extensions
diff --git a/docs/frontend-system/building-apps/06-plugin-conversion.md b/docs/frontend-system/building-apps/06-plugin-conversion.md
new file mode 100644
index 0000000000..5e7d431e73
--- /dev/null
+++ b/docs/frontend-system/building-apps/06-plugin-conversion.md
@@ -0,0 +1,87 @@
+---
+id: plugin-conversion
+title: Converting 3rd-party plugins from the old system
+sidebar_label: Converting 3rd-party Plugins
+# prettier-ignore
+description: Documentation for how to convert 3rd-party plugins to support the new frontend system.
+---
+
+If you are using or want to use a 3rd-party plugin that does not yet support the new frontend system in your app, you can often use conversion utilities from `@backstage/core-compat-api` in order wrap the plugin to make it possible to install in your app.
+
+> [!CAUTION]
+> The purpose of these utilities is to wrap 3rd-party plugins. Do not use them for your own plugins where you can add support for the new frontend system directly.
+
+## Converting a legacy plugin
+
+The `@backstage/core-compat-api` package exports the `convertLegacyPlugin` function that can be used to convert a legacy plugin to the new frontend system. Simply pass in the old plugin instance and the new plugin will be returned.
+
+```ts
+import { techdocsPlugin } from '@backstage/plugin-techdocs';
+
+// TechDocs is used as an example here, it already supports
+// the new frontend system so don't use this code directly.
+const convertedTechdocsPlugin = convertLegacyPlugin(techdocsPlugin, {
+ extensions: [],
+});
+```
+
+Note that when using `convertLegacyPlugin` we also have to pass an array of extensions. This is because the `convertLegacyPlugin` function does not have access to or know about the different types of extensions in the old system. You instead need to manually convert each of the old extensions that you want to include in the plugin instance.
+
+## Converting legacy extensions
+
+As mentioned above, you need to manually convert each of the old extensions that you want to include in the plugin instance. The `@backstage/core-compat-api` package exports `convertLegacyPageExtension` for this purpose, which can be used to convert a top-level page extension to the new system. Simply pass in the old extension and the new extension will be returned.
+
+```ts
+const convertedIndexPage = convertLegacyPageExtension(TechDocsIndexPage);
+```
+
+### Overriding inferred parameters
+
+The conversion functions such as `convertLegacyPageExtension` will attempt to infer parameters from the old extension, in particular it uses the name of the old extension to determine things like the name and route paths of the new extension. In some cases you may need to supply your own parameters to the conversion function if the inferred values are not correct.
+
+```ts
+const convertedIndexPage = convertLegacyPageExtension(TechDocsIndexPage, {
+ name: 'index',
+ defaultPath: '/docs',
+});
+```
+
+### Other types of extensions
+
+The `@backstage/core-compat-api` only provides conversion functions for the built-in extension types, i.e. only page extensions. There are many plugins that define their own extension types, such as the entity content and card extensions for the catalog. For those cases it's up to the plugin libraries to provide their own conversion functions for these types of extensions. For example, `@backstage/plugin-catalog-react/alpha` provides both `convertLegacyEntityContentExtension` and `convertLegacyEntityCardExtension`.
+
+The following are the known conversion functions provided by various libraries:
+
+- `convertLegacyPageExtension` - `@backstage/core-compat-api`
+- `convertLegacyEntityContentExtension` - `@backstage/plugin-catalog-react/alpha`
+- `convertLegacyEntityCardExtension` - `@backstage/plugin-catalog-react/alpha`
+
+## Putting it all together
+
+Using the plugin converter along with extension converters from various libraries, we can not more fully convert our 3rd-party plugin to be able to install it in an app built with the new frontend system:
+
+```ts
+import {
+ techdocsPlugin,
+ TechDocsIndexPage,
+ TechDocsReaderPage,
+ EntityTechdocsContent,
+} from '@backstage/plugin-techdocs';
+
+const convertedTechdocsPlugin = convertLegacyPlugin(techdocsPlugin, {
+ extensions: [
+ convertLegacyPageExtension(TechDocsIndexPage, {
+ name: 'index',
+ defaultPath: '/docs',
+ }),
+ convertLegacyPageExtension(TechDocsReaderPage, {
+ defaultPath: '/docs/:namespace/:kind/:name/*',
+ }),
+ convertLegacyEntityContentExtension(EntityTechdocsContent),
+ ],
+});
+
+const app = createApp({
+ features: [convertedTechdocsPlugin],
+});
+```
diff --git a/docs/frontend-system/building-apps/08-migrating.md b/docs/frontend-system/building-apps/08-migrating.md
index 0abdd2aac3..ceb3f34c35 100644
--- a/docs/frontend-system/building-apps/08-migrating.md
+++ b/docs/frontend-system/building-apps/08-migrating.md
@@ -23,7 +23,7 @@ import { createApp } from '@backstage/frontend-app-api';
This immediate switch will lead to a lot of breakages that we need to fix.
-Let's start by addressing the change to `app.createRoot(...)`, which no longer accepts any arguments. This represents a fundamental change that the new frontend system introduces. In the old system the app element tree that you passed to `app.createRoot(...)` was the primary way that you installed and configured plugins and features in your app. In the new system this is instead replaced by extensions that are wired together into an extension tree. Much more responsibility has now been shifted to plugins, for example you no longer have to manually provide the route path for each plugin page, but instead only configure it if you want to override the default. For more information on how the new system works, see the [architecture](../architecture/01-index.md) section.
+Let's start by addressing the change to `app.createRoot(...)`, which no longer accepts any arguments. This represents a fundamental change that the new frontend system introduces. In the old system the app element tree that you passed to `app.createRoot(...)` was the primary way that you installed and configured plugins and features in your app. In the new system this is instead replaced by extensions that are wired together into an extension tree. Much more responsibility has now been shifted to plugins, for example you no longer have to manually provide the route path for each plugin page, but instead only configure it if you want to override the default. For more information on how the new system works, see the [architecture](../architecture/00-index.md) section.
Given that the app element tree is most of what builds up the app, it's likely also going to be the majority of the migration effort. In order to make the migration as smooth as possible we have provided a helper that lets you convert an existing app element tree into plugins that you can install in a new app. This in turn allows for a gradual migration of individual plugins, rather than needing to migrate the entire app structure at once.
@@ -95,7 +95,7 @@ At this point the contents of your app should be past the initial migration stag
## Migrating `createApp` Options
-Many of the `createApp` options have been migrated to use extensions instead. Each will have their own [extension creator](../architecture/03-extensions.md#extension-creators) that you use to create a custom extension. To add these standalone extensions to the app they need to be passed to `createExtensionOverrides`, which bundles them into a _feature_ that you can install in the app. See the [standalone extensions](../architecture/05-extension-overrides.md#create-standalone-extensions) section for more information.
+Many of the `createApp` options have been migrated to use extensions instead. Each will have their own [extension blueprint](../architecture/23-extension-blueprints.md) that you use to create a custom extension. To add these standalone extensions to the app they need to be passed to `createExtensionOverrides`, which bundles them into a _feature_ that you can install in the app. See the [standalone extensions](../architecture/25-extension-overrides.md#creating-a-standalone-extension-bundle) section for more information.
For example, assuming you have a `lightTheme` extension that you want to add to your app, you can use the following:
@@ -115,7 +115,7 @@ You can then also add any additional extensions that you may need to create as p
### `apis`
-[Utility API](../utility-apis/01-index.md) factories are now installed as extensions instead. Pass the existing factory to `createApiExtension` and install it in the app. For more information, see the section on [configuring Utility APIs](../utility-apis/04-configuring.md).
+[Utility API](../utility-apis/01-index.md) factories are now installed as extensions instead. Pass the existing factory to `ApiBlueprint` and install it in the app. For more information, see the section on [configuring Utility APIs](../utility-apis/04-configuring.md).
For example, the following `apis` configuration:
@@ -134,12 +134,15 @@ const app = createApp({
Can be converted to the following extension:
```ts
-const scmIntegrationsApi = createApiExtension({
- factory: createApiFactory({
- api: scmIntegrationsApiRef,
- deps: { configApi: configApiRef },
- factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi),
- }),
+const scmIntegrationsApi = ApiBlueprint.make({
+ name: 'scm-integrations',
+ params: {
+ factory: createApiFactory({
+ api: scmIntegrationsApiRef,
+ deps: { configApi: configApiRef },
+ factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi),
+ }),
+ },
});
```
@@ -176,7 +179,7 @@ createApp({
});
```
-Plugins don't even have to be imported manually after installing their package if [features discovery](../architecture/02-app.md#feature-discovery) is enabled.
+Plugins don't even have to be imported manually after installing their package if [features discovery](../architecture/10-app.md#feature-discovery) is enabled.
```yaml title="in app-config.yaml"
app:
@@ -207,7 +210,7 @@ createApp({
Can be converted to the following plugin configuration:
```tsx
-createPlugin({
+createFrontendPlugin({
id: 'tech-radar',
// ...
featureFlags: [{ name: 'tech-radar' }],
@@ -219,9 +222,9 @@ createPlugin({
Many app components are now installed as extensions instead using `createComponentExtension`. See the section on [configuring app components](./01-index.md#configure-your-app) for more information.
-The `Router` component is now a built-in extension that you can [override](../architecture/05-extension-overrides.md) using `createRouterExtension`.
+The `Router` component is now a built-in extension that you can [override](../architecture/25-extension-overrides.md) using `createRouterExtension`.
-The Sign-in page is now installed as an extension using the `createSignInPageExtension` instead.
+The Sign-in page is now installed as an extension, created using the `SignInPageBlueprint` instead.
For example, the following sign-in page configuration:
@@ -246,25 +249,27 @@ const app = createApp({
Can be converted to the following extension:
```tsx
-const signInPage = createSignInPageExtension({
- loader: async () => props =>
- (
-
- ),
+const signInPage = SignInPageBlueprint.make({
+ params: {
+ loader: async () => props =>
+ (
+
+ ),
+ },
});
```
### `themes`
-Themes are now installed as extensions, using `createThemeExtension`.
+Themes are now installed as extensions, created using `ThemeBlueprint`.
For example, the following theme configuration:
@@ -287,14 +292,19 @@ const app = createApp({
Can be converted to the following extension:
```tsx
-const lightTheme = createThemeExtension({
- id: 'light',
- title: 'Light Theme',
- variant: 'light',
- icon: ,
- Provider: ({ children }) => (
-
- ),
+const lightTheme = ThemeBlueprint.make({
+ name: 'light',
+ params: {
+ theme: {
+ id: 'light',
+ title: 'Light Theme',
+ variant: 'light',
+ icon: ,
+ Provider: ({ children }) => (
+
+ ),
+ },
+ },
});
```
@@ -314,9 +324,34 @@ const app = createApp({
});
```
+### `icons`
+
+Icons are now installed as extensions, using the `IconBundleBlueprint` to make new instances which can be added to the app.
+
+```ts
+import { IconBundleBlueprint } from '@backstage/frontend-plugin-api';
+
+const exampleIconBundle = IconBundleBlueprint.make({
+ name: 'example-bundle',
+ params: {
+ icons: {
+ user: MyOwnUserIcon,
+ },
+ },
+});
+
+const app = createApp({
+ features: [
+ createExtensionOverrides({
+ extensions: [exampleIconBundle],
+ }),
+ ],
+});
+```
+
### `bindRoutes`
-Route bindings can still be done using this option, but you now also have the ability to bind routes using static configuration instead. See the section on [binding routes](../architecture/07-routes.md#binding-external-route-references) for more information.
+Route bindings can still be done using this option, but you now also have the ability to bind routes using static configuration instead. See the section on [binding routes](../architecture/36-routes.md#binding-external-route-references) for more information.
Note that if you are binding routes from a legacy plugin that was converted using `convertLegacyApp`, you will need to use the `convertLegacyRouteRefs` and/or `convertLegacyRouteRef` to convert the routes to be compatible with the new system.
@@ -335,7 +370,7 @@ const app = createApp({
### `__experimentalTranslations`
-Translations are now installed as extensions, using `createTranslationExtension`.
+Translations are now installed as extensions, created using `TranslationBlueprint`.
For example, the following translations configuration:
@@ -358,11 +393,14 @@ createApp({
Can be converted to the following extension:
```tsx
-createTranslationExtension({
- resource: createTranslationMessages({
- ref: catalogTranslationRef,
- catalog_page_create_button_title: 'Create Software',
- }),
+TranslationBlueprint.make({
+ name: 'catalog-overrides',
+ params: {
+ resource: createTranslationMessages({
+ ref: catalogTranslationRef,
+ catalog_page_create_button_title: 'Create Software',
+ }),
+ },
});
```
@@ -445,7 +483,7 @@ const routes = (
);
```
-If you are using [app feature discovery](../architecture/02-app.md#feature-discovery) the installation step is simple, it's already done! The new version of the scaffolder plugin was already discovered and present in the app, it was simply disabled because the plugin created from the legacy route had higher priority. If you do not use feature discovery, you will instead need to manually install the new scaffolder plugin in your app through the `features` option of `createApp`.
+If you are using [app feature discovery](../architecture/10-app.md#feature-discovery) the installation step is simple, it's already done! The new version of the scaffolder plugin was already discovered and present in the app, it was simply disabled because the plugin created from the legacy route had higher priority. If you do not use feature discovery, you will instead need to manually install the new scaffolder plugin in your app through the `features` option of `createApp`.
Continue this process for each of your legacy routes until you have migrated all of them. For any plugin with additional extensions installed as children of the `Route`, refer to the plugin READMEs for more detailed instructions. For the entity pages, refer to the [separate section](#entity-pages).
@@ -457,24 +495,22 @@ The entity pages are typically defined in `packages/app/src/components/catalog`
New apps feature a built-in sidebar extension (`app/nav`) that will render all nav item extensions provided by plugins. This is a placeholder implementation and not intended as a long-term solution. In the future we will aim to provide a more flexible sidebar extension that allows for more customization out of the box.
-Because the built-in sidebar is quite limited you may want to override the sidebar with your own custom implementation. To do so, use `createExtension` directly and refer to the [original sidebar implementation](https://github.com/backstage/backstage/blob/master/packages/frontend-app-api/src/extensions/AppNav.tsx). The following is an example of how to take your existing sidebar from the `Root` component that you typically find in `packages/app/src/components/Root.tsx`, and use it in an [extension override](../architecture/05-extension-overrides.md):
+Because the built-in sidebar is quite limited you may want to override the sidebar with your own custom implementation. To do so, use `createExtension` directly and refer to the [original sidebar implementation](https://github.com/backstage/backstage/blob/master/packages/frontend-app-api/src/extensions/AppNav.tsx). The following is an example of how to take your existing sidebar from the `Root` component that you typically find in `packages/app/src/components/Root.tsx`, and use it in an [extension override](../architecture/25-extension-overrides.md):
```tsx
const nav = createExtension({
namespace: 'app',
name: 'nav',
attachTo: { id: 'app/layout', input: 'nav' },
- output: {
- element: coreExtensionData.reactElement,
- },
+ output: [coreExtensionData.reactElement],
factory({ inputs }) {
- return {
- element: (
+ return [
+ coreExtensionData.reactElement(
{/* Sidebar contents from packages/app/src/components/Root.tsx go here */}
-
+ ,
),
- };
+ ];
},
});
```
@@ -523,7 +559,7 @@ export default app.createRoot(
);
```
-Any app root wrapper needs to be migrated to be an extension, using `createAppRootWrapperExtension`. Note that if you have multiple wrappers they must be completely independent of each other, i.e. the order in which they the appear in the React tree should not matter. If that is not the case then you should group them into a single wrapper.
+Any app root wrapper needs to be migrated to be an extension, created using `AppRootWrapperBlueprint`. Note that if you have multiple wrappers they must be completely independent of each other, i.e. the order in which they the appear in the React tree should not matter. If that is not the case then you should group them into a single wrapper.
Here is an example converting the `CustomAppBarrier` into extension:
@@ -533,11 +569,13 @@ createApp({
features: [
createExtensionOverrides({
extensions: [
- createAppRootWrapperExtension({
- name: 'CustomAppBarrier',
- // Whenever your component uses legacy core packages, wrap it with "compatWrapper"
- // e.g. props => compatWrapper()
- Component: CustomAppBarrier,
+ AppRootWrapperBlueprint.make({
+ name: 'custom-app-barrier',
+ params: {
+ // Whenever your component uses legacy core packages, wrap it with "compatWrapper"
+ // e.g. props => compatWrapper()
+ Component: CustomAppBarrier,
+ },
}),
],
}),
diff --git a/docs/frontend-system/building-plugins/01-index.md b/docs/frontend-system/building-plugins/01-index.md
index f8d80f9382..57a783d406 100644
--- a/docs/frontend-system/building-plugins/01-index.md
+++ b/docs/frontend-system/building-plugins/01-index.md
@@ -8,9 +8,9 @@ description: Building frontend plugins using the new frontend system
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
-This section covers how to build your own frontend [plugins](../architecture/04-plugins.md) and
-[overrides](../architecture/05-extension-overrides.md). They are sometimes collectively referred to as
-frontend _features_, and what you install to build up a Backstage frontend [app](../architecture/02-app.md).
+This section covers how to build your own frontend [plugins](../architecture/15-plugins.md) and
+[overrides](../architecture/25-extension-overrides.md). They are sometimes collectively referred to as
+frontend _features_, and what you install to build up a Backstage frontend [app](../architecture/10-app.md).
## Creating a new plugin
@@ -24,14 +24,14 @@ The created plugin will currently be templated for use in the legacy frontend sy
## The plugin instance
-The starting point of a frontend plugin is the `createPlugin` function, which accepts a single options object as its only parameter. It is imported from `@backstage/frontend-plugin-api`, which is where you will find most of the common APIs for building plugins.
+The starting point of a frontend plugin is the `createFrontendPlugin` function, which accepts a single options object as its only parameter. It is imported from `@backstage/frontend-plugin-api`, which is where you will find most of the common APIs for building plugins.
This is how to create a minimal plugin:
```tsx title="in src/plugin.ts"
-import { createPlugin } from '@backstage/frontend-plugin-api';
+import { createFrontendPlugin } from '@backstage/frontend-plugin-api';
-export const examplePlugin = createPlugin({
+export const examplePlugin = createFrontendPlugin({
id: 'example',
extensions: [],
});
@@ -43,13 +43,13 @@ export { examplePlugin as default } from './plugin';
Note that we export the plugin as the default export of our package from `src/index.ts`. This is important, as it is how users of our plugin are able to seamlessly install the plugin package in a Backstage app without having to reference the plugin instance through code.
-The plugin ID should be a lowercase dash-separated string, while the plugin instance variable should be the camel case version of the ID with a `Plugin` suffix. For more details on naming patterns within the frontend system, see [the article on naming patterns](../architecture/08-naming-patterns.md). By sticking to these naming patterns you ensure that users of your plugin more easily recognize the exports and features provided by your plugin.
+The plugin ID should be a lowercase dash-separated string, while the plugin instance variable should be the camel case version of the ID with a `Plugin` suffix. For more details on naming patterns within the frontend system, see [the article on naming patterns](../architecture/50-naming-patterns.md). By sticking to these naming patterns you ensure that users of your plugin more easily recognize the exports and features provided by your plugin.
## Adding extensions
-The plugin that we created above is empty, and doesn't provide any actual functionality. To add functionality to a plugin you need to create and provide it with one or more [extensions](../architecture/03-extensions.md). Let's continue by adding a standalone page to our plugin, as well as a navigation item that allows users to navigate to the page.
+The plugin that we created above is empty, and doesn't provide any actual functionality. To add functionality to a plugin you need to create and provide it with one or more [extensions](../architecture/20-extensions.md). Let's continue by adding a standalone page to our plugin, as well as a navigation item that allows users to navigate to the page.
-To create a new extension you typically use pre-defined [extension creators](../architecture/03-extensions.md#extension-creators), provided either by the framework itself or by other plugins. In this case we'll need to use `createPageExtension` and `createNavItemExtension`, both from `@backstage/frontend-plugin-api`. We will also need to [create a route reference](../architecture/07-routes.md#creating-a-route-reference) to use as a reference for out page, allowing us to dynamically create URLs that link to our page.
+To create a new extension you typically use pre-defined [extension blueprints](../architecture/23-extension-blueprints.md), provided either by the framework itself or by other plugins. In this case we'll use `PageBlueprint` and `NavItemBlueprint`, both from `@backstage/frontend-plugin-api`. We will also need to [create a route reference](../architecture/36-routes.md#creating-a-route-reference) to use as a reference for our page, allowing us to dynamically create URLs that link to our page.
```tsx title="in src/routes.ts"
import { createRouteRef } from '@backstage/frontend-plugin-api';
@@ -63,7 +63,7 @@ export const rootRouteRef = createRouteRef();
```tsx title="in src/plugin.ts"
import {
- createPlugin,
+ createFrontendPlugin,
createPageExtension,
createNavItemExtension,
} from '@backstage/frontend-plugin-api';
@@ -71,28 +71,33 @@ import { rootRouteRef } from './routes';
// Note that these extensions aren't exported, only the plugin itself is.
// You can export it locally for testing purposes, but don't export it from the plugin package.
-const examplePage = createPageExtension({
- routeRef: rootRouteRef,
+const examplePage = PageBlueprint.make({
+ params: {
+ routeRef: rootRouteRef,
- // This is the default path of this page, but integrators are free to override it
- defaultPath: '/example',
+ // This is the default path of this page, but integrators are free to override it
+ defaultPath: '/example',
- // Page extensions are always dynamically loaded using React.lazy().
- // All of the functionality of this page is implemented in the
- // ExamplePage component, which is a regular React component.
- // highlight-next-line
- loader: () => import('./components/ExamplePage').then(m => ),
+ // Page extensions are always dynamically loaded using React.lazy().
+ // All of the functionality of this page is implemented in the
+ // ExamplePage component, which is a regular React component.
+ // highlight-next-line
+ loader: () =>
+ import('./components/ExamplePage').then(m => ),
+ },
});
// This nav item is provided to the app.nav extension, and will by default be rendered as a sidebar item
-const exampleNavItem = createNavItemExtension({
- routeRef: rootRouteRef,
- title: 'Example',
- icon: ExampleIcon, // Custom SvgIcon, or one from the Material UI icon library
+const exampleNavItem = NavItemBlueprint.make({
+ params: {
+ routeRef: rootRouteRef,
+ title: 'Example',
+ icon: ExampleIcon, // Custom SvgIcon, or one from the Material UI icon library
+ },
});
// The same plugin as above, now with the extensions added
-export const examplePlugin = createPlugin({
+export const examplePlugin = createFrontendPlugin({
id: 'example',
extensions: [examplePage, exampleNavItem],
// We can also make routes available to other plugins.
@@ -106,7 +111,7 @@ export const examplePlugin = createPlugin({
What we've built here is a very common type of plugin. It's a top-level tool that provides a single page, along with a method for navigating to that page. The implementation of the page component, in this case the highlighted `ExamplePage`, can be arbitrarily complex. It can be anything from a single simple information page, to a full-blown application with multiple sub-pages.
-We have also provided external access to our route reference by passing it to the plugin `routes` option. This makes it possible for app integrators to bind an external link from a different plugin to our plugin page. You can read more about how this works in the [External Route References](../architecture/07-routes.md#external-route-references) section.
+We have also provided external access to our route reference by passing it to the plugin `routes` option. This makes it possible for app integrators to bind an external link from a different plugin to our plugin page. You can read more about how this works in the [External Route References](../architecture/36-routes.md#external-route-references) section.
## Utility APIs
@@ -157,18 +162,21 @@ import {
import { exampleApiRef, DefaultExampleApi } from './api';
// highlight-add-start
-const exampleApi = createApiExtension({
- factory: createApiFactory({
- api: exampleApiRef,
- deps: {},
- factory: () => new DefaultExampleApi(),
- }),
+const exampleApi = ApiBlueprint.make({
+ name: 'example',
+ params: {
+ factory: createApiFactory({
+ api: exampleApiRef,
+ deps: {},
+ factory: () => new DefaultExampleApi(),
+ }),
+ },
});
// highlight-add-end
/* Omitted definitions for examplePage, exampleNavItem, and rootRouteRef. */
-export const examplePlugin = createPlugin({
+export const examplePlugin = createFrontendPlugin({
id: 'example',
extensions: [
// highlight-add-next-line
@@ -187,26 +195,28 @@ export const examplePlugin = createPlugin({
There are many different plugins that you can extend with additional functionality through extensions. One such plugin is [the catalog plugin](../../features/software-catalog/), one of the core features of Backstage. It lets you catalog the software in your organization, where each item in the catalog has its own page that can be populated with tools and information relating to that catalog entity. In this example we will explore how our plugin can provide such a tool to display on an entity page.
```tsx title="in src/plugin.ts - An example entity content extension"
-import { createEntityContentExtension } from '@backstage/plugin-catalog-react';
+import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
// Entity content extensions are similar to page extensions in that they are rendered at a route,
// although they also have a title to support in-line navigation between the different content.
// Just like a page extension the content is lazy loaded, and you can also provide a
// route reference if you want to be able to generate a URL that links to the content.
-const exampleEntityContent = createEntityContentExtension({
- defaultPath: 'example',
- defaultTitle: 'Example',
- loader: () =>
- import('./components/ExampleEntityContent').then(m => (
-
- )),
+const exampleEntityContent = EntityContentBlueprint.make({
+ params: {
+ defaultPath: 'example',
+ defaultTitle: 'Example',
+ loader: () =>
+ import('./components/ExampleEntityContent').then(m => (
+
+ )),
+ },
});
-export const examplePlugin = createPlugin({
+export const examplePlugin = createFrontendPlugin({
id: 'example',
extensions: [
// highlight-add-next-line
- exampleEntityContent
+ exampleEntityContent,
exampleApi,
examplePage,
exampleNavItem,
@@ -219,4 +229,4 @@ export const examplePlugin = createPlugin({
The `ExampleEntityContent` itself is again a regular React component where you can implement any functionality you want. To access the entity that the content is being rendered for, you can use the `useEntity` hook from `@backstage/plugin-catalog-react`. You can see a full list of APIs provided by the catalog React library in [the API reference](../../reference/plugin-catalog-react.md).
-For a more complete list of the different types of extensions that you can create for your plugin, see the [extension types](./03-extension-types.md) section.
+For a more complete list of the different kinds of extensions that you can create for your plugin, see the [extension blueprints](./03-common-extension-blueprints.md) section.
diff --git a/docs/frontend-system/building-plugins/02-testing.md b/docs/frontend-system/building-plugins/02-testing.md
index 26c9503c6d..225c570fbd 100644
--- a/docs/frontend-system/building-plugins/02-testing.md
+++ b/docs/frontend-system/building-plugins/02-testing.md
@@ -41,7 +41,7 @@ describe('Entity details component', () => {
});
```
-To mock [Utility APIs](../architecture/06-utility-apis.md) that are used by your component you can use the `TestApiProvider` to override individual API implementations. In the snippet below, we wrap the component within a `TestApiProvider` in order to mock the catalog client API:
+To mock [Utility APIs](../architecture/33-utility-apis.md) that are used by your component you can use the `TestApiProvider` to override individual API implementations. In the snippet below, we wrap the component within a `TestApiProvider` in order to mock the catalog client API:
```tsx
import React from 'react';
@@ -85,6 +85,8 @@ describe('Entity details component', () => {
});
```
+This pattern also works for many other context providers. An important example is the `EntityProvider` from the `@backstage/plugin-catalog-react` package, which you can use to provide a mocked entity context to the component.
+
## Testing extensions
To facilitate testing of frontend extensions, the `@backstage/frontend-test-utils` package provides a tester class which starts up an entire frontend harness, complete with a number of default features. You can then provide overrides for extensions whose behavior you need to adjust for the test run.
@@ -93,7 +95,7 @@ A number of features (frontend extensions and overrides) are also accepted by th
### Single extension
-In order to test an extension in isolation, you simply need to pass it into the tester factory, then call the render method on the returned instance:
+In order to test an extension in isolation, you can use `createExtensionTester` to create a tester instance and access the element that the extension outputs. This element can then be rendered as usual with `renderInTestApp`:
```tsx
import { screen } from '@testing-library/react';
@@ -101,90 +103,49 @@ import { createExtensionTester } from '@backstage/frontend-test-utils';
import { indexPageExtension } from './plugin';
describe('Index page', () => {
- it('should render a the index page', () => {
- createExtensionTester(indexPageExtension).render();
+ it('should render a the index page', async () => {
+ await renderInTestApp(
+ createExtensionTester(indexPageExtension).reactElement(),
+ );
expect(screen.getByText('Index Page')).toBeInTheDocument();
});
});
```
-### Extension preset
+This pattern also allows you to wrap the extension with context providers, such as the `TestApiProvider` that was introduced [above](#testing-react-components).
-There are some extensions that rely on other extensions existence, such as a page that links to another page. In that case, you can add more than one extension to the preset of features you want to render in the test, as shown below:
+Note that the `.reactElement()` method will look for the `coreExtensionData.reactElement` data in the extension outputs. If that doesn't exist and the extension outputs something else that you want to test, you can access the output data using the `.get(dataRef)` method instead.
+
+### Multiple extensions
+
+In some cases you might need to test multiple extensions together, in particular when testing inputs. In this case, you can add more extensions to the tester instance using the `.add(...)` method. It also accepts an optional options object as the second argument, which you can use to provide configuration for the extension instance.
```tsx
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { createExtensionTester } from '@backstage/frontend-test-utils';
-import { indexPageExtension, detailsPageExtension } from './plugin';
+import { indexPageExtension, indexPageHeader } from './plugin';
describe('Index page', async () => {
- it('should link to the details page', () => {
- createExtensionTester(indexPageExtension)
- // Adding more extensions to the preset being tested
- .add(detailsPageExtension)
- .render();
+ it('should link to the index page with header', async () => {
+ const tester = createExtensionTester(indexPageExtension)
+ // Adding the header to be rendered on the index page
+ .add(indexPageHeader);
- await expect(screen.findByText('Index Page')).toBeInTheDocument();
+ await renderInTestApp(tester.reactElement());
- await userEvent.click(screen.getByRole('link', { name: 'See details' }));
+ await expect(screen.findByText('Index page')).toBeInTheDocument();
+ await expect(screen.findByText('Index page header')).toBeInTheDocument();
- await expect(
- screen.findByText('Details Page'),
- ).resolves.toBeInTheDocument();
+ expect(
+ tester.query(indexPageHeader).get(headerDataRef),
+ ).toMatchObject(/* ... */);
});
});
```
-### Mocking apis
-
-If your extensions requires implementation of APIs that aren't wired up by default, you'll have to add overrides to the preset of features being tested:
-
-```tsx
-import { screen } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-import { createApiFactory } from '@backestage/core-plugin-api';
-import {
- createExtensionOverrides,
- configApiRef,
- analyticsApiRef,
-} from '@backstage/frontend-plugin-api';
-import {
- createExtensionTester,
- MockConfigApi,
- MockAnalyticsApi,
-} from '@backstage/frontend-test-utils';
-import { indexPageExtension } from './plugin';
-
-describe('Index page', () => {
- it('should capture click events in analytics', async () => {
- // Mocking the analytics api implementation
- const analyticsApiMock = new MockAnalyticsApi();
-
- const analyticsApiOverride = createApiExtension({
- factory: createApiFactory({
- api: analyticsApiRef,
- factory: () => analyticsApiMock,
- }),
- });
-
- createExtensionTester(indexPageExtension)
- // Overriding the analytics api extension
- .add(analyticsApiOverride)
- .render();
-
- await userEvent.click(
- await screen.findByRole('link', { name: 'See details' }),
- );
-
- expect(analyticsApiMock.getEvents()[0]).toMatchObject({
- action: 'click',
- subject: 'See details',
- });
- });
-});
-```
+When testing multiple extensions you may sometimes want to access the output of other extensions than the main test subject. You can use the `.query(ext)` method to query a different extension that has been added to the tester, by passing the extension used with the `createExtensionTester(...).add(ext)`
### Setting configuration
@@ -198,36 +159,26 @@ import { indexPageExtension, detailsPageExtension } from './plugin';
describe('Index page', () => {
it('should accepts a custom title via config', async () => {
- createExtensionTester(indexPageExtension, {
- // Configuration specific of index page
- config: { title: 'Custom index' },
- })
- .add(detailsExtensionPage, {
- // Configuration specific of details page
- config: { title: 'Custom details' },
- })
- .render({
- // Configuration specific of the instance
- config: {
- app: {
- title: 'Custom app',
- },
+ const tester = createExtensionTester(indexPageExtension, {
+ // Extension configuration for the index page
+ config: { title: 'Custom page' },
+ }).add(indexPageHeader, {
+ // Extension configuration for the index page header
+ config: { title: 'Custom page header' },
+ });
+
+ await renderInTestApp(tester.reactElement(), {
+ // Global configuration for the app
+ config: {
+ app: {
+ title: 'Custom app',
},
- });
+ },
+ });
- await expect(
- screen.findByRole('heading', { name: 'Custom app' }),
- ).resolves.toBeInTheDocument();
-
- await expect(
- screen.findByRole('heading', { name: 'Custom index' }),
- ).resolves.toBeInTheDocument();
-
- await userEvent.click(screen.getByRole('link', { name: 'See details' }));
-
- await expect(
- screen.findByText('Custom details'),
- ).resolves.toBeInTheDocument();
+ await expect(screen.findByText('Custom app')).toBeInTheDocument();
+ await expect(screen.findByText('Custom page')).toBeInTheDocument();
+ await expect(screen.findByText('Custom page header')).toBeInTheDocument();
});
});
```
diff --git a/docs/frontend-system/building-plugins/03-extension-types.md b/docs/frontend-system/building-plugins/03-common-extension-blueprints.md
similarity index 57%
rename from docs/frontend-system/building-plugins/03-extension-types.md
rename to docs/frontend-system/building-plugins/03-common-extension-blueprints.md
index 2bea073ed6..5193fd85b1 100644
--- a/docs/frontend-system/building-plugins/03-extension-types.md
+++ b/docs/frontend-system/building-plugins/03-common-extension-blueprints.md
@@ -1,59 +1,63 @@
---
-id: extension-types
-title: Frontend System Extension Types
-sidebar_label: Extension Types
+id: common-extension-blueprints
+title: Common Extension Blueprints
+sidebar_label: Common Extension Blueprints
# prettier-ignore
-description: Extension types provided by the frontend system and core features
+description: Extension blueprints provided by the frontend system and core features
---
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
-This section covers many of the [extension types](../architecture/03-extensions.md#extension-creators) available at your disposal when building Backstage frontend plugins.
+This section covers many of the [extension blueprints](../architecture/23-extension-blueprints.md) available at your disposal when building Backstage frontend plugins.
-## Built-in extension types
+## Built-in extension blueprints
-These are the extension types provided by the Backstage frontend framework itself.
+These are the [extension blueprints](../architecture/23-extension-blueprints.md) provided by the Backstage frontend framework itself.
-### Api - [Reference](../../reference/frontend-plugin-api.createapiextension.md)
+### Api - [Reference](../../reference/frontend-plugin-api.apiblueprint.md)
An API extension is used to add or override [Utility API factories](../utility-apis/01-index.md) in the app. They are commonly used by plugins for both internal and shared APIs. There are also many built-in Api extensions provided by the framework that you are able to override.
### Component - [Reference](../../reference/frontend-plugin-api.createcomponentextension.md)
-Components extensions are used to override the component associated with a component reference throughout the app.
+Components extensions are used to override the component associated with a component reference throughout the app. This uses an extension creator function rather than a blueprint, but will likely be migrated to a blueprint in the future.
-### NavItem - [Reference](../../reference/frontend-plugin-api.createnavitemextension.md)
+### NavItem - [Reference](../../reference/frontend-plugin-api.navitemblueprint.md)
Navigation item extensions are used to provide menu items that link to different parts of the app. By default nav items are attached to the app nav extension, which by default is rendered as the left sidebar in the app.
-### Page - [Reference](../../reference/frontend-plugin-api.createpageextension.md)
+### Page - [Reference](../../reference/frontend-plugin-api.pageblueprint.md)
Page extensions provide content for a particular route in the app. By default pages are attached to the app routes extensions, which renders the root routes.
-### SignInPage - [Reference](../../reference/frontend-plugin-api.createsigninpageextension.md)
+### SignInPage - [Reference](../../reference/frontend-plugin-api.signinpageblueprint.md)
Sign-in page extension have a single purpose - to implement a custom sign-in page. They are always attached to the app root extension and are rendered before the rest of the app until the user is signed in.
-### Theme - [Reference](../../reference/frontend-plugin-api.createthemeextension.md)
+### Theme - [Reference](../../reference/frontend-plugin-api.themeblueprint.md)
Theme extensions provide custom themes for the app. They are always attached to the app extension and you can have any number of themes extensions installed in an app at once, letting the user choose which theme to use.
-### Translation - [Reference](../../reference/frontend-plugin-api.createtranslationextension.md)
+### Icons - [Reference](../../reference/frontend-plugin-api.iconbundleblueprint.md)
+
+Icon bundle extensions provide the ability to replace or provide new icons to the app. You can use the above blueprint to make new extension instances which can be installed into the app.
+
+### Translation - [Reference](../../reference/frontend-plugin-api.translationblueprint.md)
Translation extension provide custom translation messages for the app. They can be used both to override the default english messages to custom ones, as well as provide translations for additional languages.
-## Core feature extension types
+## Core feature extension blueprints
-These are the extension types provided by the Backstage core feature plugins.
+These are the [extension blueprints](../architecture/23-extension-blueprints.md) provided by the Backstage core feature plugins.
### EntityCard - [Reference](https://github.com/backstage/backstage/blob/master/plugins/catalog-react/api-report-alpha.md)
-Creates entity cards to be displayed on the entity pages of the catalog plugin.
+Creates entity cards to be displayed on the entity pages of the catalog plugin. Exported as `EntityCardBlueprint`.
### EntityContent - [Reference](https://github.com/backstage/backstage/blob/master/plugins/catalog-react/api-report-alpha.md)
-Creates entity content to be displayed on the entity pages of the catalog plugin.
+Creates entity content to be displayed on the entity pages of the catalog plugin. Exported as `EntityContentBlueprint`.
### SearchResultListItem - [Reference](https://github.com/backstage/backstage/blob/master/plugins/search-react/api-report-alpha.md)
-Creates search result list items for different types of search results, to be displayed in search result lists.
+Creates search result list items for different types of search results, to be displayed in search result lists. Exported as `SearchResultListItemBlueprint`.
diff --git a/docs/frontend-system/building-plugins/04-built-in-data-refs.md b/docs/frontend-system/building-plugins/04-built-in-data-refs.md
index dacbbf30f1..a26f6549b7 100644
--- a/docs/frontend-system/building-plugins/04-built-in-data-refs.md
+++ b/docs/frontend-system/building-plugins/04-built-in-data-refs.md
@@ -8,7 +8,7 @@ description: Configuring or overriding built-in extension data references
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
-To have a better understanding of extension data references please read [the corresponding architecture section](../architecture/03-extensions.md#extension-data) first.
+To have a better understanding of extension data references please read [the corresponding architecture section](../architecture/20-extensions.md#extension-data) first.
## Built-in extension data references
@@ -24,18 +24,15 @@ The `reactElement` data reference can be used for defining the extension input/o
```tsx
import {
+ createExtension,
coreExtensionData,
- createExtensionInput,
- createPageExtension,
} from '@backstage/frontend-plugin-api';
-const homePage = createPageExtension({
- defaultPath: '/home',
- routeRef: rootRouteRef,
- inputs: {
- props: createExtensionInput({
- children: coreExtensionData.reactElement.optional(),
- }),
+const examplePage = createExtension({
+ name: 'example',
+ output: [coreExtensionData.reactElement],
+ factor() {
+ return [coreExtensionData.reactElement(
Example
)];
},
});
```
diff --git a/docs/frontend-system/building-plugins/05-migrating.md b/docs/frontend-system/building-plugins/05-migrating.md
index 16f465ece7..cf4d82002a 100644
--- a/docs/frontend-system/building-plugins/05-migrating.md
+++ b/docs/frontend-system/building-plugins/05-migrating.md
@@ -8,7 +8,7 @@ description: How to migrate an existing frontend plugin to the new frontend syst
This guide allows you to migrate a frontend plugin and its own components, routes, apis to the new frontend system.
-The main concept is that routes, components, apis are now extensions. You can use the appropriate extension creators to migrate all of them to extensions.
+The main concept is that routes, components, apis are now extensions. You can use the appropriate [extension blueprints](../architecture/23-extension-blueprints.md) to migrate all of them to extensions.
## Migrating the plugin
@@ -29,13 +29,13 @@ In the legacy frontend system a plugin was defined in its own `plugin.ts` file a
});
```
-In order to migrate the actual definition of the plugin you need to recreate the plugin using the new `createPlugin` utility exported by `@backstage/frontend-plugin-api`.
-The new `createPlugin` function doesn't accept apis anymore as apis are now extensions.
+In order to migrate the actual definition of the plugin you need to recreate the plugin using the new `createFrontendPlugin` utility exported by `@backstage/frontend-plugin-api`.
+The new `createFrontendPlugin` function doesn't accept apis anymore as apis are now extensions.
```ts title="my-plugin/src/alpha.ts"
- import { createPlugin } from '@backstage/frontend-plugin-api';
+ import { createFrontendPlugin } from '@backstage/frontend-plugin-api';
- export default createPlugin({
+ export default createFrontendPlugin({
id: 'my-plugin',
// bind all the extensions to the plugin
/* highlight-next-line */
@@ -77,7 +77,7 @@ The code above binds all the extensions to the plugin. _Important_: Make sure to
## Migrating Pages
-Pages that were previously created using the `createRoutableExtension` extension function can be migrated to the new Frontend System using the `createPageExtension` extension creator, exported by `@backstage/frontend-plugin-api`.
+Pages that were previously created using the `createRoutableExtension` extension function can be migrated to the new Frontend System using the `PageBlueprint` [extension blueprint](../architecture/23-extension-blueprints.md), exported by `@backstage/frontend-plugin-api`.
For example, given the following page:
@@ -94,33 +94,35 @@ export const FooPage = fooPlugin.provide(
it can be migrated as the following:
```tsx
-import { createPageExtension } from '@backstage/frontend-plugin-api';
+import { PageBlueprint } from '@backstage/frontend-plugin-api';
import {
compatWrapper,
convertLegacyRouteRef,
} from '@backstage/core-compat-api';
-const fooPage = createPageExtension({
- defaultPath: '/foo',
- // you can reuse the existing routeRef
- // by wrapping into the convertLegacyRouteRef.
- routeRef: convertLegacyRouteRef(rootRouteRef),
- // these inputs usually match the props required by the component.
- loader: ({ inputs }) =>
- import('./components/').then(m =>
- // The compatWrapper utility allows you to use the existing
- // legacy frontend utilities used internally by the components.
- compatWrapper(),
- ),
+const fooPage = PageBlueprint.make({
+ params: {
+ defaultPath: '/foo',
+ // you can reuse the existing routeRef
+ // by wrapping into the convertLegacyRouteRef.
+ routeRef: convertLegacyRouteRef(rootRouteRef),
+ // these inputs usually match the props required by the component.
+ loader: ({ inputs }) =>
+ import('./components/').then(m =>
+ // The compatWrapper utility allows you to use the existing
+ // legacy frontend utilities used internally by the components.
+ compatWrapper(),
+ ),
+ },
});
```
-then add the `fooPage` extension to the plugin:
+Then add the `fooPage` extension to the plugin:
```ts title="my-plugin/src/alpha.ts"
- import { createPlugin } from '@backstage/frontend-plugin-api';
+ import { createFrontendPlugin } from '@backstage/frontend-plugin-api';
- export default createPlugin({
+ export default createFrontendPlugin({
id: 'my-plugin',
// bind all the extensions to the plugin
/* highlight-remove-next-line */
@@ -133,7 +135,7 @@ then add the `fooPage` extension to the plugin:
## Migrating Components
-The equivalent utility to replace components created with `createComponentExtension` is `createExtension` from `@backstage/frontend-plugin-api`. However, we recommend searching for a more appropriate extension creator first.
+The equivalent utility to replace components created with `createComponentExtension` depends on the context within which the component is used, typically indicated by the naming pattern of the export. Many of these can be migrated to one of the [existing blueprints](03-common-extension-blueprints.md), but in rare cases it may be necessary to use [`createExtension`](../architecture/20-extensions.md#creating-an-extension) directly.
## Migrating APIs
@@ -182,7 +184,7 @@ const exampleWorkApi = createApiFactory({
The major changes we'll make are
- Change the old `@backstage/core-plugin-api` imports to the new `@backstage/frontend-plugin-api` package as per the top section of this guide
-- Wrap the existing API factory in a `createApiExtension`
+- Wrap the existing API factory in a `ApiBlueprint`
The end result, after simplifying imports and cleaning up a bit, might look like this:
@@ -190,26 +192,28 @@ The end result, after simplifying imports and cleaning up a bit, might look like
import {
storageApiRef,
createApiFactory,
- createApiExtension,
+ ApiBlueprint,
} from '@backstage/frontend-plugin-api';
import { workApiRef } from '@internal/plugin-example-react';
import { WorkImpl } from './WorkImpl';
-const exampleWorkApi = createApiExtension({
- factory: createApiFactory({
- api: workApiRef,
- deps: { storageApi: storageApiRef },
- factory: ({ storageApi }) => new WorkImpl({ storageApi }),
- }),
+const exampleWorkApi = ApiBlueprint.make({
+ params: {
+ factory: createApiFactory({
+ api: workApiRef,
+ deps: { storageApi: storageApiRef },
+ factory: ({ storageApi }) => new WorkImpl({ storageApi }),
+ }),
+ },
});
```
Finally, let's add the `exampleWorkApi` extension to the plugin:
```ts title="my-plugin/src/alpha.ts"
- import { createPlugin } from '@backstage/frontend-plugin-api';
+ import { createFrontendPlugin } from '@backstage/frontend-plugin-api';
- export default createPlugin({
+ export default createFrontendPlugin({
id: 'my-plugin',
// bind all the extensions to the plugin
/* highlight-remove-next-line */
diff --git a/docs/frontend-system/utility-apis/01-index.md b/docs/frontend-system/utility-apis/01-index.md
index 37120a5152..07f7dcc2a8 100644
--- a/docs/frontend-system/utility-apis/01-index.md
+++ b/docs/frontend-system/utility-apis/01-index.md
@@ -8,7 +8,7 @@ description: Working with Utility APIs in the New Frontend System
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
-As described [in the architecture section](../architecture/06-utility-apis.md), utility APIs are pieces of shared functionality - interfaces that can be requested by plugins to use. They are defined by a TypeScript interface as well as a reference (an "API ref") used to access its implementation. They can be provided both by plugins and the core framework, and are themselves [extensions](../architecture/03-extensions.md) that can accept inputs, be declaratively configured in your app-config, or transparently be replaced entirely with custom implementations that fulfill the same contract.
+As described [in the architecture section](../architecture/33-utility-apis.md), utility APIs are pieces of shared functionality - interfaces that can be requested by plugins to use. They are defined by a TypeScript interface as well as a reference (an "API ref") used to access its implementation. They can be provided both by plugins and the core framework, and are themselves [extensions](../architecture/20-extensions.md) that can accept inputs, be declaratively configured in your app-config, or transparently be replaced entirely with custom implementations that fulfill the same contract.
## Creating utility APIs
diff --git a/docs/frontend-system/utility-apis/02-creating.md b/docs/frontend-system/utility-apis/02-creating.md
index fcb7ff1a65..01c21fe656 100644
--- a/docs/frontend-system/utility-apis/02-creating.md
+++ b/docs/frontend-system/utility-apis/02-creating.md
@@ -45,9 +45,9 @@ The plugin itself now wants to provide this API and its default implementation,
```tsx title="in @internal/plugin-example"
import {
- createApiExtension,
+ ApiBlueprint,
createApiFactory,
- createPlugin,
+ createFrontendPlugin,
storageApiRef,
StorageApi,
} from '@backstage/frontend-plugin-api';
@@ -62,21 +62,24 @@ class WorkImpl implements WorkApi {
}
}
-const exampleWorkApi = createApiExtension({
- factory: createApiFactory({
- api: workApiRef,
- deps: { storageApi: storageApiRef },
- factory: ({ storageApi }) => {
- return new WorkImpl({ storageApi });
- },
- }),
+const workApi = ApiBlueprint.make({
+ name: 'work',
+ params: {
+ factory: createApiFactory({
+ api: workApiRef,
+ deps: { storageApi: storageApiRef },
+ factory: ({ storageApi }) => {
+ return new WorkImpl({ storageApi });
+ },
+ }),
+ },
});
/**
* The Example plugin.
* @public
*/
-export default createPlugin({
+export default createFrontendPlugin({
id: 'example',
extensions: [exampleWorkApi],
});
@@ -86,43 +89,37 @@ For illustration we make a skeleton implementation class and the API extension a
The code also illustrates how the API factory declares a dependency on another utility API - the core storage API in this case. An instance of that utility API is then provided to the factory function.
-The resulting extension ID of the work API will be the kind `api:` followed by the plugin ID as the namespace, in this case ending up as `api:plugin.example.work`. Check out [the naming patterns doc](../architecture/08-naming-patterns.md) for more information on how this works. You can now use this ID to refer to the API in app-config and elsewhere.
+The extension ID of the work API will be the kind `api:` followed by the plugin ID as the namespace, a `/` separator, and lastly the name we used of the extension. In this case we end up with `api:example/work`. Check out [the naming patterns doc](../architecture/50-naming-patterns.md) for more information on how this works. You can now use this ID to refer to the API in app-config and elsewhere. In case there is a single API that is a central to the functionality of the plugin, most typically an API client, you can choose to omit the name of the extension so that you end up with just `api:`.
## Adding configurability
-Here we will describe how to amend a utility API with the capability of having extension config, which is driven by [your app-config](../../conf/writing.md). You do this by giving an extension config schema to your API extension factory function. Let's make the required additions to our original work example API.
+Here we will describe how to amend a utility API with the capability of having extension config, which is driven by [your app-config](../../conf/writing.md). You do this by giving an extension config schema to your API extension factory function. Let's refactory the example above to also accept configuration, which will require us to use the [override method of the blueprint](../architecture/23-extension-blueprints.md#creating-an-extension-from-a-blueprint-with-overrides).
```tsx title="in @internal/plugin-example"
-/* highlight-add-next-line */
-import { createSchemaFromZod } from '@backstage/frontend-plugin-api';
-
-const exampleWorkApi = createApiExtension({
- /* highlight-add-start */
- api: workApiRef,
- configSchema: createSchemaFromZod(z =>
- z.object({
- goSlow: z.boolean().default(false),
- }),
- ),
- /* highlight-add-end */
- /* highlight-remove-next-line */
- factory: createApiFactory({
- /* highlight-add-next-line */
- factory: ({ config }) => createApiFactory({
- api: workApiRef,
- deps: { storageApi: storageApiRef },
- factory: ({ storageApi }) => {
- /* highlight-add-start */
- if (config.goSlow) {
- /* ... */
- }
- /* highlight-add-end */
+const exampleWorkApi = ApiBlueprint.makeWithOverrides({
+ config: {
+ schema: {
+ goSlow: z => z.boolean().default(false),
},
- }),
+ },
+ factory(originalFactory, { config }) {
+ return originalFactory({
+ factory: createApiFactory({
+ api: workApiRef,
+ deps: { storageApi: storageApiRef },
+ factory: ({ storageApi }) => {
+ return new WorkImpl({
+ storageApi,
+ goSlow: config.goSlow,
+ });
+ },
+ }),
+ });
+ },
});
```
-We wanted users to be able to set a `goSlow` extension config parameter for our API instances. So we passed in a `configSchema` to `createApiExtension` which matches that interface. This example builds it using [the zod library](https://zod.dev/). The actual extension config values will then be passed in a type safe manner in to the `factory` which is now a callback, wherein we can do what we wish with them. When changing to the callback form, we also had to add a top level `api: workApiRef` under `createApiExtension`.
+We wanted users to be able to set a `goSlow` extension config parameter for our API instances, which we declared in our new configuration schema. The actual extension config values will then be passed in a type safe manner in to the blueprint `factory`, wherein we can use them to create our API factory and pass as our blueprint parameters.
Note that the expression "extension config" as used here, is _not_ the same thing as the `configApi` which gives you access to the full app-config. The extension config discussed here is instead the particular configuration settings given to your utility API instance. This is discussed more [in the Configuring section](./04-configuring.md).
@@ -130,11 +127,11 @@ Note also that the extension config schema contained a default value fo the `goS
## Adding inputs
-Inputs are added to Utility APIs in the same way as other extension types:
+Inputs are added to Utility APIs in the same way as other extension blueprints:
-- Declaring a set of `inputs` on your extension
-- If needed, create custom extension data types to be used in those inputs
-- If needed, export an extension creator function for creating that particular attachment type
+- Use `.makeWithOverrides` and declare a set of `inputs` for your extension.
+- If needed, create custom extension data types to be used in those inputs.
+- If needed, create and export an [extension blueprint](../architecture/23-extension-blueprints.md#creating-an-extension-blueprint) for creating that particular attachment type.
This is a power use case and not very commonly used.
diff --git a/docs/frontend-system/utility-apis/03-consuming.md b/docs/frontend-system/utility-apis/03-consuming.md
index 5c53c8617e..bdf3b8dcdb 100644
--- a/docs/frontend-system/utility-apis/03-consuming.md
+++ b/docs/frontend-system/utility-apis/03-consuming.md
@@ -46,23 +46,25 @@ Your utility APIs can depend on other utility APIs in their factories. You do th
```tsx
import {
configApiRef,
- createApiExtension,
+ ApiBlueprint,
createApiFactory,
discoveryApiRef,
} from '@backstage/frontend-plugin-api';
import { MyApiImpl } from './MyApiImpl';
-const myApi = createApiExtension({
- factory: createApiFactory({
- api: myApiRef,
- deps: {
- configApi: configApiRef,
- discoveryApi: discoveryApiRef,
- },
- factory: ({ configApi, discoveryApi }) => {
- return new MyApiImpl({ configApi, discoveryApi });
- },
- }),
+const myApi = ApiBlueprint.make({
+ params: {
+ factory: createApiFactory({
+ api: myApiRef,
+ deps: {
+ configApi: configApiRef,
+ discoveryApi: discoveryApiRef,
+ },
+ factory: ({ configApi, discoveryApi }) => {
+ return new MyApiImpl({ configApi, discoveryApi });
+ },
+ }),
+ },
});
```
diff --git a/docs/frontend-system/utility-apis/04-configuring.md b/docs/frontend-system/utility-apis/04-configuring.md
index 7acf23decf..e403104506 100644
--- a/docs/frontend-system/utility-apis/04-configuring.md
+++ b/docs/frontend-system/utility-apis/04-configuring.md
@@ -12,7 +12,7 @@ Utility APIs are extensions and can therefore optionally be amended with configu
## Configuring
-To configure your Utility API extension, first you'll need to know its ID. That ID is formed from the API ref ID; check [the naming patterns docs](../architecture/08-naming-patterns.md) for details.
+To configure your Utility API extension, first you'll need to know its ID. That ID is formed from the API ref ID; check [the naming patterns docs](../architecture/50-naming-patterns.md) for details.
Our example work API from [the creating section](./02-creating.md) would have the ID `api:plugin.example.work`. You configure it and all other extensions under the `app.extensions` section of your app-config.
@@ -37,7 +37,7 @@ Well written input-enabled extension often have extension creator functions that
## Replacing a Utility API implementation
-Like with other extension types, you replace Utility APIs with your own custom implementation using [extension overrides](../architecture/05-extension-overrides.md).
+Like with other extension types, you replace Utility APIs with your own custom implementation using [extension overrides](../architecture/25-extension-overrides.md).
```tsx title="in your app"
/* highlight-add-start */
@@ -49,13 +49,13 @@ class CustomWorkImpl implements WorkApi {
const myOverrides = createExtensionOverrides({
extensions: [
- createApiExtension({
- api: workApiRef,
- factory: () =>
- createApiFactory({
+ ApiBlueprint.make({
+ params: {
+ factory: createApiFactory({
api: workApiRef,
factory: () => new CustomWorkImpl(),
}),
+ },
}),
],
});
diff --git a/docs/getting-started/config/authentication.md b/docs/getting-started/config/authentication.md
index 874964696a..e97ed3d89c 100644
--- a/docs/getting-started/config/authentication.md
+++ b/docs/getting-started/config/authentication.md
@@ -50,7 +50,6 @@ Open `packages/app/src/App.tsx` and below the last `import` line, add:
```typescript title="packages/app/src/App.tsx"
import { githubAuthApiRef } from '@backstage/core-plugin-api';
-import { SignInPage } from '@backstage/core-components';
```
Search for `const app = createApp({` in this file, and below `apis,` add:
diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md
index 99fdb3d8ed..8bb30d1db8 100644
--- a/docs/getting-started/index.md
+++ b/docs/getting-started/index.md
@@ -1,6 +1,6 @@
---
id: index
-title: Installing a standalone server
+title: Creating your Backstage App
sidebar_label: Introduction
description: How to install Backstage for your own use.
---
diff --git a/docs/getting-started/keeping-backstage-updated.md b/docs/getting-started/keeping-backstage-updated.md
index 1de8874d56..88e4a02ef2 100644
--- a/docs/getting-started/keeping-backstage-updated.md
+++ b/docs/getting-started/keeping-backstage-updated.md
@@ -63,18 +63,16 @@ When a given dependency version is the _same_ between different packages, the
dependency is hoisted to the main `node_modules` folder in the monorepo root to
be shared between packages. When _different_ versions of the same dependency are
encountered, Yarn creates a `node_modules` folder within a particular package.
+This can lead to multiple versions of the same package being installed and used
+in the same app.
-This can lead to confusing situations with type definitions, or anything with
-global state. React [Context](https://reactjs.org/docs/context.html), for
-example, depends on global referential equality. This can cause problems in
-Backstage with API lookup, or config loading.
+All Backstage core packages are implemented in such as way that package
+duplication is **not** a problem. For example, duplicate installations of
+packages like `@backstage/core-plugin-api`, `@backstage/core-components`,
+`@backstage/plugin-catalog-react`, and `@backstage/backend-plugin-api` are all
+acceptable.
-To help resolve these situations, the Backstage CLI has
-[versions:check](https://backstage.io/docs/tooling/cli/03-commands#versionscheck). This
-will validate versions of `@backstage` packages in your app to check for
-duplicate definitions:
-
-```bash
-# Add --fix to attempt automatic resolution in yarn.lock
-yarn backstage-cli versions:check
-```
+While package duplication might be acceptable in many cases, you might want to
+deduplicate packages for the purpose of optimizing bundle size and installation
+speed. We recommend using deduplication utilities such as `yarn dedupe` to trim
+down the number of duplicate packages.
diff --git a/docs/getting-started/logging-in.md b/docs/getting-started/logging-in.md
index fef8907e07..7d6fb5d55b 100644
--- a/docs/getting-started/logging-in.md
+++ b/docs/getting-started/logging-in.md
@@ -20,6 +20,8 @@ Run your Backstage app with `yarn dev`. Navigate to `http://localhost:3000`.
If you're not already logged in, you should see a login screen like this,
+
+
To login, you should choose the "Github" provider and click the "Sign in" button. This will redirect you to a Github OAuth page. Verify that the scopes mentioned on that page match the setup you did in [the authentication tutorial](./config/authentication.md). Once you click "Confirm", you will be brought back to the Backstage interface and signed in!
If you are already logged in, you will be automatically brought to your Backstage instance.
diff --git a/docs/integrations/aws-s3/discovery--old.md b/docs/integrations/aws-s3/discovery--old.md
index 09b6eb6cc0..3a9d30a370 100644
--- a/docs/integrations/aws-s3/discovery--old.md
+++ b/docs/integrations/aws-s3/discovery--old.md
@@ -36,7 +36,7 @@ catalog:
bucketName: sample-bucket
prefix: prefix/ # optional
region: us-east-2 # optional, uses the default region otherwise
- schedule: # same options as in TaskScheduleDefinition
+ schedule: # same options as in SchedulerServiceTaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
@@ -56,7 +56,7 @@ catalog:
bucketName: sample-bucket
prefix: prefix/ # optional
region: us-east-2 # optional, uses the default region otherwise
- schedule: # same options as in TaskScheduleDefinition
+ schedule: # same options as in SchedulerServiceTaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
diff --git a/docs/integrations/aws-s3/discovery.md b/docs/integrations/aws-s3/discovery.md
index db9119680e..4cca448cf2 100644
--- a/docs/integrations/aws-s3/discovery.md
+++ b/docs/integrations/aws-s3/discovery.md
@@ -36,7 +36,7 @@ catalog:
bucketName: sample-bucket
prefix: prefix/ # optional
region: us-east-2 # optional, uses the default region otherwise
- schedule: # same options as in TaskScheduleDefinition
+ schedule: # same options as in SchedulerServiceTaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
@@ -56,7 +56,7 @@ catalog:
bucketName: sample-bucket
prefix: prefix/ # optional
region: us-east-2 # optional, uses the default region otherwise
- schedule: # same options as in TaskScheduleDefinition
+ schedule: # same options as in SchedulerServiceTaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
diff --git a/docs/integrations/azure/discovery--old.md b/docs/integrations/azure/discovery--old.md
index 56a2fe559f..27532fe6ee 100644
--- a/docs/integrations/azure/discovery--old.md
+++ b/docs/integrations/azure/discovery--old.md
@@ -45,7 +45,7 @@ catalog:
project: myproject
repository: service-* # this will match all repos starting with service-*
path: /catalog-info.yaml
- schedule: # optional; same options as in TaskScheduleDefinition
+ schedule: # optional; same options as in SchedulerServiceTaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
diff --git a/docs/integrations/azure/discovery.md b/docs/integrations/azure/discovery.md
index c8a8a3af7c..357df7d553 100644
--- a/docs/integrations/azure/discovery.md
+++ b/docs/integrations/azure/discovery.md
@@ -45,7 +45,7 @@ catalog:
project: myproject
repository: service-* # this will match all repos starting with service-*
path: /catalog-info.yaml
- schedule: # optional; same options as in TaskScheduleDefinition
+ schedule: # optional; same options as in SchedulerServiceTaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
diff --git a/docs/integrations/bitbucketCloud/discovery.md b/docs/integrations/bitbucketCloud/discovery.md
index 04dc111d30..6ba9c0aa1f 100644
--- a/docs/integrations/bitbucketCloud/discovery.md
+++ b/docs/integrations/bitbucketCloud/discovery.md
@@ -146,7 +146,7 @@ catalog:
filters: # optional
projectKey: '^apis-.*$' # optional; RegExp
repoSlug: '^service-.*$' # optional; RegExp
- schedule: # same options as in TaskScheduleDefinition
+ schedule: # same options as in SchedulerServiceTaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
diff --git a/docs/integrations/bitbucketServer/discovery--old.md b/docs/integrations/bitbucketServer/discovery--old.md
new file mode 100644
index 0000000000..d8ef11533b
--- /dev/null
+++ b/docs/integrations/bitbucketServer/discovery--old.md
@@ -0,0 +1,123 @@
+---
+id: discovery
+title: Bitbucket Server Discovery
+sidebar_label: Discovery
+# prettier-ignore
+description: Automatically discovering catalog entities from repositories in Bitbucket Server
+---
+
+:::info
+This documentation is written for the old backend which has been replaced by [the new backend system](../../backend-system/index.md), being the default since Backstage [version 1.24](../../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./discovery.md) instead. Otherwise, [consider migrating](../../backend-system/building-backends/08-migrating.md)!
+:::
+
+The Bitbucket Server integration has a special entity provider for discovering
+catalog files located in Bitbucket Server.
+The provider will search your Bitbucket Server account and register catalog files matching the configured path
+as Location entity and via following processing steps add all contained catalog entities.
+This can be useful as an alternative to static locations or manually adding things to the catalog.
+
+## Installation
+
+You will have to add the entity provider in the catalog initialization code of your
+backend. The provider is not installed by default, therefore you have to add a
+dependency to `@backstage/plugin-catalog-backend-module-bitbucket-server` to your backend
+package.
+
+```bash
+# From your Backstage root directory
+yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-bitbucket-server
+```
+
+And then add the entity provider to your catalog builder:
+
+```ts title="packages/backend/src/plugins/catalog.ts"
+/* highlight-add-next-line */
+import { BitbucketServerEntityProvider } from '@backstage/plugin-catalog-backend-module-bitbucket-server';
+
+export default async function createPlugin(
+ env: PluginEnvironment,
+): Promise {
+ const builder = await CatalogBuilder.create(env);
+ /* highlight-add-start */
+ builder.addEntityProvider(
+ BitbucketServerEntityProvider.fromConfig(env.config, {
+ logger: env.logger,
+ scheduler: env.scheduler,
+ }),
+ );
+ /* highlight-add-end */
+
+ // ..
+}
+```
+
+## Configuration
+
+To use the entity provider, you'll need a [Bitbucket Server integration set up](locations.md).
+
+Additionally, you need to configure your entity provider instance(s):
+
+```yaml title="app-config.yaml"
+catalog:
+ providers:
+ bitbucketServer:
+ yourProviderId: # identifies your ingested dataset
+ host: 'bitbucket.mycompany.com'
+ catalogPath: /catalog-info.yaml # default value
+ filters: # optional
+ projectKey: '^apis-.*$' # optional; RegExp
+ repoSlug: '^service-.*$' # optional; RegExp
+ skipArchivedRepos: true # optional; boolean
+ schedule: # 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`**:
+ 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)_:
+ - **`projectKey`** _(optional)_:
+ Regular expression used to filter results based on the project key.
+ - **`repoSlug`** _(optional)_:
+ Regular expression used to filter results based on the repo slug.
+ - **`skipArchivedRepos`** _(optional)_:
+ Boolean flag to filter out archived repositories.
+- **`schedule`**:
+ - **`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
+
+The Bitbucket Server Entity Provider will by default emit a location for each
+matching repository. However, it is possible to override this functionality and take full control of how each
+matching repository is processed.
+
+`BitbucketServerEntityProvider.fromConfig` takes an optional parameter
+`options.parser` where you can set your own parser to be used for each matched
+repository.
+
+```typescript
+const provider = BitbucketServerEntityProvider.fromConfig(env.config, {
+ logger: env.logger,
+ schedule: env.scheduler,
+ parser: async function* customLocationParser(options: {
+ location: LocationSpec;
+ client: BitbucketServerClient;
+ }) {
+ // Custom logic for interpreting the matching repository
+ // See defaultBitbucketServerLocationParser for an example
+ },
+});
+```
diff --git a/docs/integrations/bitbucketServer/discovery.md b/docs/integrations/bitbucketServer/discovery.md
index 99e04affa8..3bc37e4cb5 100644
--- a/docs/integrations/bitbucketServer/discovery.md
+++ b/docs/integrations/bitbucketServer/discovery.md
@@ -6,6 +6,10 @@ sidebar_label: Discovery
description: Automatically discovering catalog entities from repositories in Bitbucket Server
---
+:::info
+This documentation is written for [the new backend system](../../backend-system/index.md) which is the default since Backstage [version 1.24](../../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./discovery--old.md) instead, and [consider migrating](../../backend-system/building-backends/08-migrating.md)!
+:::
+
The Bitbucket Server integration has a special entity provider for discovering
catalog files located in Bitbucket Server.
The provider will search your Bitbucket Server account and register catalog files matching the configured path
@@ -16,34 +20,21 @@ This can be useful as an alternative to static locations or manually adding thin
You will have to add the entity provider in the catalog initialization code of your
backend. The provider is not installed by default, therefore you have to add a
-dependency to `@backstage/plugin-catalog-backend-module-bitbucket-server` to your backend
-package.
+dependency to `@backstage/plugin-catalog-backend-module-bitbucket-server` to your backend package.
```bash title="From your Backstage root directory"
yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-bitbucket-server
```
-And then add the entity provider to your catalog builder:
+And update your backend by adding the following line:
-```ts title="packages/backend/src/plugins/catalog.ts"
-/* highlight-add-next-line */
-import { BitbucketServerEntityProvider } from '@backstage/plugin-catalog-backend-module-bitbucket-server';
-
-export default async function createPlugin(
- env: PluginEnvironment,
-): Promise {
- const builder = await CatalogBuilder.create(env);
- /* highlight-add-start */
- builder.addEntityProvider(
- BitbucketServerEntityProvider.fromConfig(env.config, {
- logger: env.logger,
- scheduler: env.scheduler,
- }),
- );
- /* highlight-add-end */
-
- // ..
-}
+```ts title="packages/backend/src/index.ts"
+backend.add(import('@backstage/plugin-catalog-backend/alpha'));
+/* highlight-add-start */
+backend.add(
+ import('@backstage/plugin-catalog-backend-module-bitbucket-server/alpha'),
+);
+/* highlight-add-end */
```
## Configuration
@@ -63,7 +54,7 @@ catalog:
projectKey: '^apis-.*$' # optional; RegExp
repoSlug: '^service-.*$' # optional; RegExp
skipArchivedRepos: true # optional; boolean
- schedule: # same options as in TaskScheduleDefinition
+ schedule: # same options as in SchedulerServiceTaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
@@ -92,27 +83,3 @@ catalog:
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
-
-The Bitbucket Server Entity Provider will by default emit a location for each
-matching repository. However, it is possible to override this functionality and take full control of how each
-matching repository is processed.
-
-`BitbucketServerEntityProvider.fromConfig` takes an optional parameter
-`options.parser` where you can set your own parser to be used for each matched
-repository.
-
-```typescript
-const provider = BitbucketServerEntityProvider.fromConfig(env.config, {
- logger: env.logger,
- schedule: env.scheduler,
- parser: async function* customLocationParser(options: {
- location: LocationSpec;
- client: BitbucketServerClient;
- }) {
- // Custom logic for interpreting the matching repository
- // See defaultBitbucketServerLocationParser for an example
- },
-});
-```
diff --git a/docs/integrations/datadog-rum/installation.md b/docs/integrations/datadog-rum/installation.md
index 4dc6c24134..6659bec182 100644
--- a/docs/integrations/datadog-rum/installation.md
+++ b/docs/integrations/datadog-rum/installation.md
@@ -28,6 +28,51 @@ app:
If your [`app-config.yaml`](https://github.com/backstage/backstage/blob/e0506af8fc54074a160fb91c83d6cae8172d3bb3/app-config.yaml#L5) file does not have this configuration, you may have to adjust your [`packages/app/public/index.html`](https://github.com/backstage/backstage/blob/e0506af8fc54074a160fb91c83d6cae8172d3bb3/packages/app/public/index.html#L69) to include the Datadog RUM `init()` section manually.
+Please note that the env value MUST be specified at build time
+
+:::note
+In case after a proper configuration, the events still are not being captured: Copy and paste this section in to your `packages/app/public/index.html` under the `` tag.
+
+```html
+<% if (config.has('app.datadogRum')) { %>
+
+<% } %>
+```
+
The `clientToken` and `applicationId` are generated from the Datadog RUM page
following
[these instructions](https://docs.datadoghq.com/real_user_monitoring/browser/).
diff --git a/docs/integrations/gerrit/discovery--old.md b/docs/integrations/gerrit/discovery--old.md
new file mode 100644
index 0000000000..250de80bdf
--- /dev/null
+++ b/docs/integrations/gerrit/discovery--old.md
@@ -0,0 +1,73 @@
+---
+id: discovery--old
+title: Gerrit Discovery
+sidebar_label: Discovery
+# prettier-ignore
+description: Automatically discovering catalog entities from Gerrit repositories
+---
+
+:::info
+This documentation is written for the old backend which has been replaced by [the new backend system](../../backend-system/index.md), being the default since Backstage [version 1.24](../../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./discovery.md) instead. Otherwise, [consider migrating](../../backend-system/building-backends/08-migrating.md)!
+:::
+
+The Gerrit integration has a special entity provider for discovering catalog entities
+from Gerrit repositories. The provider uses the "List Projects" API in Gerrit to get
+a list of repositories and will automatically ingest all `catalog-info.yaml` files
+stored in the root of the matching projects.
+
+## Installation
+
+As this provider is not one of the default providers, you will first need to install
+the Gerrit provider plugin:
+
+```bash title="From your Backstage root directory"
+yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-gerrit
+```
+
+Then add the plugin to the plugin catalog `packages/backend/src/plugins/catalog.ts`:
+
+```ts
+/* packages/backend/src/plugins/catalog.ts */
+import { GerritEntityProvider } from '@backstage/plugin-catalog-backend-module-gerrit';
+const builder = await CatalogBuilder.create(env);
+/** ... other processors and/or providers ... */
+builder.addEntityProvider(
+ GerritEntityProvider.fromConfig(env.config, {
+ logger: env.logger,
+ scheduler: env.scheduler,
+ }),
+);
+```
+
+## Configuration
+
+To use the discovery processor, you'll need a Gerrit integration
+[set up](locations.md). Then you can add any number of providers.
+
+```yaml
+# app-config.yaml
+catalog:
+ providers:
+ gerrit:
+ yourProviderId: # identifies your dataset / provider independent of config changes
+ host: gerrit-your-company.com
+ branch: master # Optional
+ query: 'state=ACTIVE&prefix=webapps'
+ schedule:
+ # 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
+ query: 'state=ACTIVE&prefix=backend'
+```
+
+The provider configuration is composed of three parts:
+
+- **`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/gerrit/discovery.md b/docs/integrations/gerrit/discovery.md
index 96dfbd47fb..eca0417871 100644
--- a/docs/integrations/gerrit/discovery.md
+++ b/docs/integrations/gerrit/discovery.md
@@ -6,6 +6,10 @@ sidebar_label: Discovery
description: Automatically discovering catalog entities from Gerrit repositories
---
+:::info
+This documentation is written for [the new backend system](../../backend-system/index.md) which is the default since Backstage [version 1.24](../../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./discovery--old.md) instead, and [consider migrating](../../backend-system/building-backends/08-migrating.md)!
+:::
+
The Gerrit integration has a special entity provider for discovering catalog entities
from Gerrit repositories. The provider uses the "List Projects" API in Gerrit to get
a list of repositories and will automatically ingest all `catalog-info.yaml` files
@@ -20,19 +24,13 @@ the Gerrit provider plugin:
yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-gerrit
```
-Then add the plugin to the plugin catalog `packages/backend/src/plugins/catalog.ts`:
+Then update your backend by adding the following line:
-```ts
-/* packages/backend/src/plugins/catalog.ts */
-import { GerritEntityProvider } from '@backstage/plugin-catalog-backend-module-gerrit';
-const builder = await CatalogBuilder.create(env);
-/** ... other processors and/or providers ... */
-builder.addEntityProvider(
- GerritEntityProvider.fromConfig(env.config, {
- logger: env.logger,
- scheduler: env.scheduler,
- }),
-);
+```ts title="packages/backend/src/index.ts"
+backend.add(import('@backstage/plugin-catalog-backend/alpha'));
+/* highlight-add-start */
+backend.add(import('@backstage/plugin-catalog-backend-module-gerrit/alpha'));
+/* highlight-add-end */
```
## Configuration
diff --git a/docs/integrations/github/discovery--old.md b/docs/integrations/github/discovery--old.md
index 752aa51bde..901462b1cb 100644
--- a/docs/integrations/github/discovery--old.md
+++ b/docs/integrations/github/discovery--old.md
@@ -133,7 +133,7 @@ catalog:
filters:
branch: 'main' # string
repository: '.*' # Regex
- schedule: # same options as in TaskScheduleDefinition
+ schedule: # same options as in SchedulerServiceTaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
@@ -249,7 +249,7 @@ schedule:
timeout: { minutes: 3 }
```
-More information about scheduling can be found on the [TaskScheduleDefinition](https://backstage.io/docs/reference/backend-tasks.taskscheduledefinition) page.
+More information about scheduling can be found on the [SchedulerServiceTaskScheduleDefinition](https://backstage.io/docs/reference/backend-plugin-api.schedulerservicetaskscheduledefinition) page.
Alternatively, or additionally, you can configure [github-apps](github-apps.md) authentication
which carries a much higher rate limit at GitHub.
diff --git a/docs/integrations/github/discovery.md b/docs/integrations/github/discovery.md
index 43e63a618c..b2e998813d 100644
--- a/docs/integrations/github/discovery.md
+++ b/docs/integrations/github/discovery.md
@@ -83,7 +83,7 @@ catalog:
filters:
branch: 'main' # string
repository: '.*' # Regex
- schedule: # same options as in TaskScheduleDefinition
+ schedule: # same options as in SchedulerServiceTaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
@@ -203,7 +203,7 @@ schedule:
timeout: { minutes: 3 }
```
-More information about scheduling can be found on the [TaskScheduleDefinition](https://backstage.io/docs/reference/backend-tasks.taskscheduledefinition) page.
+More information about scheduling can be found on the [SchedulerServiceTaskScheduleDefinition](https://backstage.io/docs/reference/backend-plugin-api.schedulerservicetaskscheduledefinition) page.
Alternatively, or additionally, you can configure [github-apps](github-apps.md) authentication
which carries a much higher rate limit at GitHub.
diff --git a/docs/integrations/github/org.md b/docs/integrations/github/org.md
index 18865cd630..61e42567a9 100644
--- a/docs/integrations/github/org.md
+++ b/docs/integrations/github/org.md
@@ -94,7 +94,7 @@ Directly under the `githubOrg` is a list of configurations, each entry is a stru
- `id`: A stable id for this provider. Entities from this provider will be associated with this ID, so you should take care not to change it over time since that may lead to orphaned entities and/or conflicts.
- `githubUrl`: The target that this provider should consume
- `orgs` (optional): The list of the GitHub orgs to consume. If you only list a single org the generated group entities will use the `default` namespace, otherwise they will use the org name as the namespace. By default the provider will consume all accessible orgs on the given GitHub instance (support for GitHub App integration only).
-- `schedule`: The refresh schedule to use, matches the structure of [`TaskScheduleDefinitionConfig`](https://backstage.io/docs/reference/backend-tasks.taskscheduledefinitionconfig/)
+- `schedule`: The refresh schedule to use, matches the structure of [`SchedulerServiceTaskScheduleDefinitionConfig`](https://backstage.io/docs/reference/backend-plugin-api.schedulerservicetaskscheduledefinitionconfig/)
### Events Support
diff --git a/docs/integrations/gitlab/discovery.md b/docs/integrations/gitlab/discovery.md
index 13ffd79801..79bd37061a 100644
--- a/docs/integrations/gitlab/discovery.md
+++ b/docs/integrations/gitlab/discovery.md
@@ -153,7 +153,8 @@ catalog:
group: example-group # Optional. Group and subgroup (if needed) to look for repositories. If not present the whole instance 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]*`, which means to not filter anything
- schedule: # Same options as in TaskScheduleDefinition. Optional for the Legacy Backend System
+ excludeRepos: [] # Optional. A list of project paths that should be excluded from discovery, e.g. group/subgroup/repo. Should not start or end with a slash.
+ schedule: # Same options as in SchedulerServiceTaskScheduleDefinition. Optional for the Legacy Backend System
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
diff --git a/docs/integrations/gitlab/org.md b/docs/integrations/gitlab/org.md
index 65f66aa763..24b64d1116 100644
--- a/docs/integrations/gitlab/org.md
+++ b/docs/integrations/gitlab/org.md
@@ -173,7 +173,7 @@ catalog:
group: org/teams # Required for gitlab.com when `orgEnabled: true`. Optional for self managed. Must not end with slash. Accepts only groups under the provided path (which will be stripped)
allowInherited: true # Allow groups to be ingested even if there are no direct members.
groupPattern: '[\s\S]*' # Optional. Filters found groups based on provided pattern. Defaults to `[\s\S]*`, which means to not filter anything
- schedule: # Same options as in TaskScheduleDefinition. Optional for the Legacy Backend System.
+ schedule: # Same options as in SchedulerServiceTaskScheduleDefinition. Optional for the Legacy Backend System.
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
diff --git a/docs/notifications/index.md b/docs/notifications/index.md
new file mode 100644
index 0000000000..95d77a9b7f
--- /dev/null
+++ b/docs/notifications/index.md
@@ -0,0 +1,320 @@
+---
+id: index
+title: Getting Started
+description: How to get started with the notifications and signals
+---
+
+The Backstage Notifications System provides a way for plugins and external services to send notifications to Backstage users.
+These notifications are displayed in the dedicated page of the Backstage frontend UI or by frontend plugins per specific scenarios.
+Additionally, notifications can be sent to external channels (like email) via "processors" implemented within plugins.
+
+Notifications can be optionally integrated with the signals (a push mechanism) to ensure users receive them immediately.
+
+### Upgrade to the latest version of Backstage
+
+To ensure your version of Backstage has all the latest notifications and signals related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade!
+
+## About notifications
+
+Notifications are messages sent to either individual users or groups.
+They are not intended for inter-process communication of any kind.
+
+There are two basic types of notifications:
+
+- **Broadcast**: Messages sent to all users of Backstage.
+- **Entity**: Messages delivered to specific listed entities, such as Users or Groups.
+
+Example of use-cases:
+
+- System-wide announcements or alerts
+- Notifications for component owners: e.g., build failures, successful deployments, new vulnerabilities
+- Notifications for individuals: e.g., updates you have subscribed to, new required training courses
+- Notifications pertaining to a particular entity in the catalog: A notification might apply to an entity and the owning team.
+
+## Installation in Older Environments
+
+Newer versions of instances created by the create-app have both the notifications and signals plugins included by default, this section can be skipped right to the Configuration.
+
+Following installation instructions are valid for enabling the plugins in older environments.
+
+### Add Notifications Backend
+
+```bash
+yarn workspace backend add @backstage/plugin-notifications-backend
+```
+
+Add the notifications to your `backend/src/index.ts`:
+
+```ts
+const backend = createBackend();
+// ...
+backend.add(import('@backstage/plugin-notifications-backend'));
+```
+
+### Add Notifications Frontend
+
+```bash
+yarn workspace app add @backstage/notifications
+```
+
+To add the notifications main menu, add following to your `packages/app/src/components/Root/Root.tsx`:
+
+```tsx
+import { NotificationsSidebarItem } from '@backstage/plugin-notifications';
+
+
+
+
+ // ...
+
+
+
+;
+```
+
+Also add the route to notifications to `packages/app/src/App.tsx`:
+
+```tsx
+import { NotificationsPage } from '@backstage/plugin-notifications';
+
+
+ // ...
+ } />
+;
+```
+
+### Optional: Add Signals Backend
+
+Optionally add Signals to your backend by
+
+```bash
+yarn workspace backend add @backstage/plugin-signals-backend
+```
+
+Add the signals to your `backend/src/index.ts`:
+
+```ts
+const backend = createBackend();
+// ...
+backend.add(import('@backstage/plugin-signals-backend'));
+```
+
+### Optional: Signals Frontend
+
+The use of signals is optional but improves user experience.
+
+Start with:
+
+```bash
+yarn workspace app add @backstage/plugin-signals
+```
+
+To install the plugin, you have to add the following to your `packages/app/src/plugins.ts`:
+
+```ts
+export { signalsPlugin } from '@backstage/plugin-signals';
+```
+
+And make sure that your `packages/app/src/App.tsx` contains:
+
+```ts
+import * as plugins from './plugins';
+
+const app = createApp({
+ // ...
+ plugins: Object.values(plugins),
+ // ...
+});
+```
+
+If the signals plugin is properly configured, it will be automatically discovered by the notifications plugin and used.
+
+## Configuration
+
+### Notifications Backend
+
+The Notifications backend plugin provides an API to create notifications, list notifications per logged-in user, and search based on parameters.
+
+The plugin uses a relational [database](https://backstage.io/docs/getting-started/config/database) for persistence, no specifics are introduced in this context.
+
+No additional configuration in the app-config is needed, except for optional additional modules for `processors`.
+
+### Notifications Frontend
+
+The recipients of notifications have to be entities in the catalog, e.g. of the User or Group kind.
+
+Otherwise no specific configuration is needed for the front-end notifications plugin.
+
+All parametrization is done through component properties, such as the `NotificationsSidebarItem`, which can be used as an active left-side menu item in the front-end.
+
+
+
+In the `packages/app/src/components/Root/Root.tsx`, tweak the [properties](https://backstage.io/docs/reference/plugin-notifications.notificationssidebaritem) of the `` per specific needs.
+
+## Use
+
+New notifications can be sent either by a backend plugin or an external service through the REST API.
+
+### Backend
+
+Regardless of technical feasibility, a backend plugin should avoid directly accessing the notifications REST API.
+Instead, it should integrate with the `@backstage/plugin-notifications-node` to `send` (create) a new notification.
+
+The reasons for this approach include the propagation of authorization in the API request and improved maintenance and backward compatibility in the future.
+
+```ts
+import { notificationService } from '@backstage/plugin-notifications-node';
+
+export const myPlugin = createBackendPlugin({
+ pluginId: 'myPlugin',
+ register(env) {
+ env.registerInit({
+ deps: {
+ // ...
+ notificationService: notificationService,
+ },
+ async init({ config, logger, httpRouter, notificationService }) {
+ httpRouter.use(
+ await createRouter({
+ // ...
+ notificationService,
+ }),
+ );
+ },
+ });
+ },
+});
+```
+
+To emit a new notification:
+
+```ts
+notificationService.send({
+ recipients /* of the broadcast or entity type */,
+ payload /* actual message */,
+});
+```
+
+Refer the [API documentation](https://github.com/backstage/backstage/blob/master/plugins/notifications-node/api-report.md) for further details.
+
+### Signals
+
+The use of signals with notifications is optional but generally enhances user experience and performance.
+
+When a notification is created, a new signal is emitted to a general-purpose message bus to announce it to subscribed listeners.
+
+The frontend maintains a persistent connection (WebSocket) to receive these announcements from the notifications channel.
+The specific details of the updated or created notification should be retrieved via a request to the notifications API, except for new notifications, where the payload is included in the signal for performance reasons.
+
+In a frontend plugin, to subscribe for notifications' signals:
+
+```ts
+import { useSignal } from '@backstage/plugin-signals-react';
+
+const { lastSignal } = useSignal('notifications');
+
+React.useEffect(() => {
+ /* ... */
+}, [lastSignal, notificationsApi]);
+```
+
+### Consuming Notifications
+
+In a front-end plugin, the simplest way to query a notification is by its ID:
+
+```ts
+import { useApi } from '@backstage/core-plugin-api';
+import { notificationsApiRef } from '@backstage/plugin-notifications';
+
+const notificationsApi = useApi(notificationsApiRef);
+
+notificationsApi.getNotification(yourId);
+
+// or with connection to signals:
+notificationsApi.getNotification(lastSignal.notification_id);
+```
+
+### Extending Notifications via Processors
+
+The notifications can be extended with `NotificationProcessor`. These processors allow to decorate notifications before they are sent or/and send the notifications to external services.
+
+Depending on the needs, a processor can modify the content of a notification or route it to different systems like email, Slack, or other services.
+
+A good example of how to write a processor is the [Email Processor](https://github.com/backstage/backstage/tree/master/plugins/notifications-backend-module-email).
+
+Start off by creating a notification processor:
+
+```ts
+import { Notification } from '@backstage/plugin-notifications-common';
+import { NotificationProcessor } from '@backstage/plugin-notifications-node';
+
+class MyNotificationProcessor implements NotificationProcessor {
+ async decorate(notification: Notification): Promise {
+ if (notification.origin === 'plugin-my-plugin') {
+ notification.payload.icon = 'my-icon';
+ }
+ return notification;
+ }
+
+ async send(notification: Notification): Promise {
+ nodemailer.sendEmail({
+ from: 'backstage',
+ to: 'user',
+ subject: notification.payload.title,
+ text: notification.payload.description,
+ });
+ }
+}
+```
+
+Both of the processing functions are optional, and you can implement only one of them.
+
+Add the notification processor to the notification system by:
+
+```ts
+import { notificationsProcessingExtensionPoint } from '@backstage/plugin-notifications-node';
+import { Notification } from '@backstage/plugin-notifications-common';
+
+export const myPlugin = createBackendPlugin({
+ pluginId: 'myPlugin',
+ register(env) {
+ env.registerInit({
+ deps: {
+ notifications: notificationsProcessingExtensionPoint,
+ // ...
+ },
+ async init({ notifications }) {
+ // ...
+ notifications.addProcessor(new MyNotificationProcessor());
+ },
+ });
+ },
+});
+```
+
+### External Services
+
+When the emitter of a notification is a Backstage backend plugin, it is mandatory to use the integration via `@backstage/plugin-notifications-node` as described above.
+
+If the emitter is a service external to Backstage, an HTTP POST request can be issued directly to the API, assuming that authentication is properly configured.
+Refer to the [service-to-service auth documentation](https://backstage.io/docs/auth/service-to-service-auth) for more details, focusing on the Static Tokens section for the simplest setup option.
+
+An example request for creating a broadcast notification might look like:
+
+```bash
+curl -X POST https://[BACKSTAGE_BACKEND]/api/notifications -H "Content-Type: application/json" -H "Authorization: Bearer YOUR_BASE64_SHARED_KEY_TOKEN" -d '{"recipients":{"type":"broadcast"},"payload": {"title": "Title of broadcast message","link": "http://foo.com/bar","severity": "high","topic": "The topic"}}'
+```
+
+## Additional info
+
+An example of a backend plugin sending notifications can be found in https://github.com/backstage/backstage/tree/master/plugins/scaffolder-backend-module-notifications.
+
+Sources of the notifications and signal plugins:
+
+- https://github.com/backstage/backstage/blob/master/plugins/notifications
+
+- https://github.com/backstage/backstage/blob/master/plugins/notifications-backend
+
+- https://github.com/backstage/backstage/blob/master/plugins/notifications-node
+
+- https://github.com/backstage/backstage/blob/master/plugins/signals-react
diff --git a/docs/notifications/notificationsPage.png b/docs/notifications/notificationsPage.png
new file mode 100644
index 0000000000..7c96ab77af
Binary files /dev/null and b/docs/notifications/notificationsPage.png differ
diff --git a/docs/overview/threat-model.md b/docs/overview/threat-model.md
index c0e2d61e25..74c055a86c 100644
--- a/docs/overview/threat-model.md
+++ b/docs/overview/threat-model.md
@@ -41,7 +41,7 @@ The built-in protection against unauthorized access does not by default include
## Common Backend Configuration
-There are many common facilities that are configured centrally and available to all Backstage backend plugins. For example there is a `DatabaseManager` that provides access to a SQL database, `TaskScheduler` for scheduling long-running tasks, `Logger` as a general logging facility, and `UrlReader` for reading content from external sources. These are all configured either directly in code, or within the `backend` block of the static configuration. The appropriate care needs to be taken to ensure that any secrets remain confidential and no malicious configuration is injected.
+There are many common facilities that are configured centrally and available to all Backstage backend plugins. For example there is a `DatabaseManager` that provides access to a SQL database, `SchedulerService` for scheduling long-running tasks, `Logger` as a general logging facility, and `UrlReader` for reading content from external sources. These are all configured either directly in code, or within the `backend` block of the static configuration. The appropriate care needs to be taken to ensure that any secrets remain confidential and no malicious configuration is injected.
In a typical Backstage setup, there is no boundary between plugins that run on the same host. Likewise, there is no boundary between plugins that share the same database access. Any plugin that is running on a host that has access to the logical database of any other plugin should be considered to have full access to that other plugin. For example, even if you deploy the `auth` and `catalog` plugins on separate hosts with separate configuration and credentials, the `catalog` plugin is still considered to have full access to the `auth` plugin as long as the `catalog` plugin has access to the `auth` plugin's logical database. The only way to create a boundary between the two plugins is to deploy them in such a way that they do not have access to each others’ database. This applies to the database facility as well as any other shared resources, such as the cache.
diff --git a/docs/permissions/custom-rules--old.md b/docs/permissions/custom-rules--old.md
new file mode 100644
index 0000000000..7661e23327
--- /dev/null
+++ b/docs/permissions/custom-rules--old.md
@@ -0,0 +1,165 @@
+---
+id: custom-rules--old
+title: Defining custom permission rules
+description: How to define custom permission rules for existing resources
+---
+
+:::info
+This documentation is written for the old backend which has been replaced by [the new backend system](../backend-system/index.md), being the default since Backstage [version 1.24](../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./custom-rules.md) instead. Otherwise, [consider migrating](../backend-system/building-backends/08-migrating.md)!
+:::
+
+For some use cases, you may want to define custom [rules](../references/glossary.md#rule-permission-plugin) in addition to the ones provided by a plugin. In the [previous section](./writing-a-policy.md) we used the `isEntityOwner` rule to control access for catalog entities. Let's extend this policy with a custom rule that checks what [system](https://backstage.io/docs/features/software-catalog/system-model#system) an entity is part of.
+
+## Define a custom rule
+
+Plugins should export a rule factory that provides type-safety that ensures compatibility with the plugin's backend. The catalog plugin exports `createCatalogPermissionRule` from `@backstage/plugin-catalog-backend/alpha` for this purpose. Note: the `/alpha` path segment is temporary until this API is marked as stable. For this example, we'll define the rule and create a condition in `packages/backend/src/plugins/permission.ts`.
+
+We use Zod in our example below. To install, run:
+
+```bash
+yarn workspace backend add zod
+```
+
+```typescript title="packages/backend/src/plugins/permission.ts"
+...
+
+import type { Entity } from '@backstage/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',
+ paramsSchema: z.object({
+ systemRef: z
+ .string()
+ .describe('SystemRef to check the resource is part of'),
+ }),
+ apply: (resource: Entity, { systemRef }) => {
+ if (!resource.relations) {
+ return false;
+ }
+
+ return resource.relations
+ .filter(relation => relation.type === 'partOf')
+ .some(relation => relation.targetRef === systemRef);
+ },
+ toQuery: ({ systemRef }) => ({
+ key: 'relations.partOf',
+ values: [systemRef],
+ }),
+});
+
+const isInSystem = createConditionFactory(isInSystemRule);
+
+...
+```
+
+For a more detailed explanation on defining rules, refer to the [documentation for plugin authors](./plugin-authors/03-adding-a-resource-permission-check.md#adding-support-for-conditional-decisions).
+
+Still in the `packages/backend/src/plugins/permission.ts` file, let's use the condition we just created in our `TestPermissionPolicy`.
+
+```ts title="packages/backend/src/plugins/permission.ts"
+...
+/* highlight-remove-next-line */
+import { createCatalogPermissionRule } from '@backstage/plugin-catalog-backend/alpha';
+/* highlight-add-next-line */
+import { catalogConditions, createCatalogConditionalDecision, createCatalogPermissionRule } from '@backstage/plugin-catalog-backend/alpha';
+/* highlight-remove-next-line */
+import { createConditionFactory } from '@backstage/plugin-permission-node';
+/* highlight-add-next-line */
+import { PermissionPolicy, PolicyQuery, PolicyQueryUser, createConditionFactory } from '@backstage/plugin-permission-node';
+/* highlight-add-start */
+import { AuthorizeResult, PolicyDecision, isResourcePermission } from '@backstage/plugin-permission-common';
+/* highlight-add-end */
+...
+
+export const isInSystemRule = createCatalogPermissionRule({
+ name: 'IS_IN_SYSTEM',
+ description: 'Checks if an entity is part of the system provided',
+ resourceType: 'catalog-entity',
+ paramsSchema: z.object({
+ systemRef: z
+ .string()
+ .describe('SystemRef to check the resource is part of'),
+ }),
+ apply: (resource: Entity, { systemRef }) => {
+ if (!resource.relations) {
+ return false;
+ }
+
+ return resource.relations
+ .filter(relation => relation.type === 'partOf')
+ .some(relation => relation.targetRef === systemRef);
+ },
+ toQuery: ({ systemRef }) => ({
+ key: 'relations.partOf',
+ values: [systemRef],
+ }),
+});
+
+const isInSystem = createConditionFactory(isInSystemRule);
+
+class TestPermissionPolicy implements PermissionPolicy {
+ async handle(
+ request: PolicyQuery,
+ user?: PolicyQueryUser,
+ ): Promise {
+ if (isResourcePermission(request.permission, 'catalog-entity')) {
+ return createCatalogConditionalDecision(
+ request.permission,
+ /* highlight-remove-start */
+ catalogConditions.isEntityOwner({
+ claims: user?.info.ownershipEntityRefs ?? [],
+ }),
+ /* highlight-remove-end */
+ /* highlight-add-start */
+ {
+ anyOf: [
+ catalogConditions.isEntityOwner({
+ claims: user?.info.ownershipEntityRefs ?? [],
+ }),
+ isInSystem({ systemRef: 'interviewing' }),
+ ],
+ },
+ /* highlight-add-end */
+ );
+ }
+
+ return { result: AuthorizeResult.ALLOW };
+ }
+}
+
+...
+```
+
+## Provide the rule during plugin setup
+
+Now that we have a custom rule defined and added to our policy, we need provide it to the catalog plugin. This step is important because the catalog plugin will use the rule's `toQuery` and `apply` methods while evaluating conditional authorize results. There's no guarantee that the catalog and permission backends are running on the same server, so we must explicitly link the rule to ensure that it's available at runtime.
+
+The api for providing custom rules may differ between plugins, but there should typically be some integration point during the creation of the backend router. For the catalog, this integration point is exposed via `CatalogBuilder.addPermissionRules`.
+
+```typescript title="packages/backend/src/plugins/catalog.ts"
+import { CatalogBuilder } from '@backstage/plugin-catalog-backend';
+/* highlight-add-next-line */
+import { isInSystemRule } from './permission';
+
+...
+
+export default async function createPlugin(
+ env: PluginEnvironment,
+): Promise {
+ const builder = await CatalogBuilder.create(env);
+ /* highlight-add-next-line */
+ builder.addPermissionRules(isInSystemRule);
+ ...
+ return router;
+}
+```
+
+The updated policy will allow catalog entity resource permissions if any of the following are true:
+
+- User owns the target entity
+- Target entity is part of the 'interviewing' system
diff --git a/docs/permissions/custom-rules.md b/docs/permissions/custom-rules.md
index 57565755fb..9642f80215 100644
--- a/docs/permissions/custom-rules.md
+++ b/docs/permissions/custom-rules.md
@@ -4,19 +4,23 @@ title: Defining custom permission rules
description: How to define custom permission rules for existing resources
---
+:::info
+This documentation is written for [the new backend system](../backend-system/index.md) which is the default since Backstage [version 1.24](../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./custom-rules--old.md) instead, and [consider migrating](../backend-system/building-backends/08-migrating.md)!
+:::
+
For some use cases, you may want to define custom [rules](../references/glossary.md#rule-permission-plugin) in addition to the ones provided by a plugin. In the [previous section](./writing-a-policy.md) we used the `isEntityOwner` rule to control access for catalog entities. Let's extend this policy with a custom rule that checks what [system](https://backstage.io/docs/features/software-catalog/system-model#system) an entity is part of.
## Define a custom rule
-Plugins should export a rule factory that provides type-safety that ensures compatibility with the plugin's backend. The catalog plugin exports `createCatalogPermissionRule` from `@backstage/plugin-catalog-backend/alpha` for this purpose. Note: the `/alpha` path segment is temporary until this API is marked as stable. For this example, we'll define the rule and create a condition in `packages/backend/src/plugins/permission.ts`.
+Plugins should export a rule factory that provides type-safety that ensures compatibility with the plugin's backend. The catalog plugin exports `createCatalogPermissionRule` from `@backstage/plugin-catalog-backend/alpha` for this purpose. Note: the `/alpha` path segment is temporary until this API is marked as stable. For this example, we'll define the rule and create a condition in `packages/backend/src/extensions/permissionsPolicyExtension.ts`.
-We use Zod in our example below. To install, run:
+We use `zod` and `@backstage/catalog-model` in our example below. To install them run:
-```bash
-yarn workspace backend add zod
+```bash title="from your Backstage root directory"
+yarn --cwd packages/backend add zod @backstage/catalog-model
```
-```typescript title="packages/backend/src/plugins/permission.ts"
+```ts title="packages/backend/src/extensions/permissionsPolicyExtension.ts"
...
import type { Entity } from '@backstage/catalog-model';
@@ -55,9 +59,9 @@ const isInSystem = createConditionFactory(isInSystemRule);
For a more detailed explanation on defining rules, refer to the [documentation for plugin authors](./plugin-authors/03-adding-a-resource-permission-check.md#adding-support-for-conditional-decisions).
-Still in the `packages/backend/src/plugins/permission.ts` file, let's use the condition we just created in our `TestPermissionPolicy`.
+Still in the `packages/backend/src/extensions/permissionsPolicyExtension.ts` file, let's use the condition we just created in our `CustomPermissionPolicy`.
-```ts title="packages/backend/src/plugins/permission.ts"
+```ts title="packages/backend/src/extensions/permissionsPolicyExtension.ts"
...
/* highlight-remove-next-line */
import { createCatalogPermissionRule } from '@backstage/plugin-catalog-backend/alpha';
@@ -98,7 +102,7 @@ export const isInSystemRule = createCatalogPermissionRule({
const isInSystem = createConditionFactory(isInSystemRule);
-class TestPermissionPolicy implements PermissionPolicy {
+class CustomPermissionPolicy implements PermissionPolicy {
async handle(
request: PolicyQuery,
user?: PolicyQueryUser,
@@ -135,25 +139,49 @@ class TestPermissionPolicy implements PermissionPolicy {
Now that we have a custom rule defined and added to our policy, we need provide it to the catalog plugin. This step is important because the catalog plugin will use the rule's `toQuery` and `apply` methods while evaluating conditional authorize results. There's no guarantee that the catalog and permission backends are running on the same server, so we must explicitly link the rule to ensure that it's available at runtime.
-The api for providing custom rules may differ between plugins, but there should typically be some integration point during the creation of the backend router. For the catalog, this integration point is exposed via `CatalogBuilder.addPermissionRules`.
+The api for providing custom rules may differ between plugins, but there should typically be an [extension point](../backend-system/architecture/05-extension-points.md) that you can use in your created module to add your rule. For the catalog, this extension point is exposed via `catalogPermissionExtensionPoint`. Here's the steps you'll need to take to add the `isInSystemRule` we created above to the catalog:
-```typescript title="packages/backend/src/plugins/catalog.ts"
-import { CatalogBuilder } from '@backstage/plugin-catalog-backend';
-/* highlight-add-next-line */
-import { isInSystemRule } from './permission';
+1. We will be using the `@backstage/plugin-catalog-node` package as it contains the extension point we need. Run this to add it:
-...
+ ```bash title="from your Backstage root directory"
+ yarn --cwd packages/backend add @backstage/plugin-catalog-node
+ ```
-export default async function createPlugin(
- env: PluginEnvironment,
-): Promise {
- const builder = await CatalogBuilder.create(env);
- /* highlight-add-next-line */
- builder.addPermissionRules(isInSystemRule);
- ...
- return router;
-}
-```
+2. Next create a `catalogPermissionRules.ts` file in the `packages/backend/src/extensions` folder.
+3. Then add this as the contents of the new `catalogPermissionRules.ts` file:
+
+ ```typescript title="packages/backend/src/extensions/catalogPermissionRules.ts"
+ import { createBackendModule } from '@backstage/backend-plugin-api';
+ import { catalogPermissionExtensionPoint } from '@backstage/plugin-catalog-node/alpha';
+ import { isInSystemRule } from './permissionPolicyExtension';
+
+ export default createBackendModule({
+ pluginId: 'catalog',
+ moduleId: 'permission-rules',
+ register(reg) {
+ reg.registerInit({
+ deps: { catalog: catalogPermissionExtensionPoint },
+ async init({ catalog }) {
+ catalog.addPermissionRules(isInSystemRule);
+ },
+ });
+ },
+ });
+ ```
+
+4. Next we need to add this to the backend by adding the following line:
+
+ ```ts title="packages/backend/src/index.ts"
+ // catalog plugin
+ backend.add(import('@backstage/plugin-catalog-backend/alpha'));
+ backend.add(
+ import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
+ );
+ /* highlight-add-next-line */
+ backend.add(import('./extensions/catalogPermissionRules'));
+ ```
+
+5. Now when you run you Backstage instance - `yarn dev` - the rule will be added to the catalog plugin.
The updated policy will allow catalog entity resource permissions if any of the following are true:
diff --git a/docs/permissions/getting-started--new.md b/docs/permissions/getting-started--new.md
deleted file mode 100644
index b6198a222f..0000000000
--- a/docs/permissions/getting-started--new.md
+++ /dev/null
@@ -1,36 +0,0 @@
----
-id: getting-started--new
-title: Getting Started
-description: How to get started with the permission framework as an integrator
----
-
-Backstage integrators control permissions by writing a policy. In general terms, a policy is simply an async function which receives a request to authorize a specific action for a user and (optional) resource, and returns a decision on whether to authorize that permission. Integrators can implement their own policies from scratch, or adopt reusable policies written by others.
-
-## Prerequisites
-
-The permissions framework depends on a few other Backstage systems, which must be set up before we can dive into writing a policy.
-
-### Upgrade to the latest version of Backstage
-
-To ensure your version of Backstage has all the latest permission-related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade!
-
-### Supply an identity resolver to populate group membership on sign in
-
-**Note**: If you are working off of an existing Backstage instance, you likely already have some form of an identity resolver set up.
-
-Like many other parts of Backstage, the permissions framework relies on information about group membership. This simplifies authoring policies through the use of groups, rather than requiring each user to be listed in the configuration. Group membership is also often useful for conditional permissions, for example allowing permissions to act on an entity to be granted when a user is a member of a group that owns that entity.
-
-[The IdentityResolver docs](../auth/identity-resolver.md) describe the process for resolving group membership on sign in.
-
-## Enable and test the permissions system
-
-All you need to do now is enable the permissions system in your Backstage instance!
-
-1. Set the property `permission.enabled` to `true` in `app-config.yaml`.
-
-```yaml title="app-config.yaml"
-permission:
- enabled: true
-```
-
-Congratulations! Now that the framework is configured, you can craft a permission policy that works best for your organization by utilizing a provided authorization method or by [writing your own policy](./writing-a-policy.md)!
diff --git a/docs/permissions/getting-started--old.md b/docs/permissions/getting-started--old.md
new file mode 100644
index 0000000000..96dbfec92f
--- /dev/null
+++ b/docs/permissions/getting-started--old.md
@@ -0,0 +1,169 @@
+---
+id: getting-started--old
+title: Getting Started
+description: How to get started with the permission framework as an integrator
+---
+
+:::info
+This documentation is written for the old backend which has been replaced by [the new backend system](../backend-system/index.md), being the default since Backstage [version 1.24](../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./getting-started.md) instead. Otherwise, [consider migrating](../backend-system/building-backends/08-migrating.md)!
+:::
+
+If you prefer to watch a video instead, you can start with this video introduction:
+
+
+
+:::note Note
+
+This video was recorded in the January 2022 Contributors Session using `@backstage/create-app@0.4.14`. Some aspects of the demo may have changed in later releases.
+
+:::
+
+Backstage integrators control permissions by writing a policy. In general terms, a policy is simply an async function which receives a request to authorize a specific action for a user and (optional) resource, and returns a decision on whether to authorize that permission. Integrators can implement their own policies from scratch, or adopt reusable policies written by others.
+
+## Prerequisites
+
+The permissions framework depends on a few other Backstage systems, which must be set up before we can dive into writing a policy.
+
+### Upgrade to the latest version of Backstage
+
+The permissions framework itself is new to Backstage and still evolving quickly. To ensure your version of Backstage has all the latest permission-related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade!
+
+### Enable service-to-service authentication
+
+Service-to-service authentication allows Backstage backend code to verify that a given request originates from elsewhere in the Backstage backend. This is useful for tasks like collation of catalog entities in the search index. This type of request shouldn’t be permissioned, so it’s important to configure this feature before trying to use the permissions framework.
+
+To set up service-to-service authentication, follow the [service-to-service authentication docs](../auth/service-to-service-auth.md).
+
+### Supply an identity resolver to populate group membership on sign in
+
+**Note**: If you are working off of an existing Backstage instance, you likely already have some form of an identity resolver set up.
+
+Like many other parts of Backstage, the permissions framework relies on information about group membership. This simplifies authoring policies through the use of groups, rather than requiring each user to be listed in the configuration. Group membership is also often useful for conditional permissions, for example allowing permissions to act on an entity to be granted when a user is a member of a group that owns that entity.
+
+[The IdentityResolver docs](../auth/identity-resolver.md) describe the process for resolving group membership on sign in.
+
+## Optionally add cookie-based authentication
+
+Asset requests initiated by the browser will not include a token in the `Authorization` header. If these requests check authorization through the permission framework, as done in plugins like TechDocs, then you'll need to set up cookie-based authentication. Refer to the ["Authenticate API requests"](https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/authenticate-api-requests.md) tutorial for a demonstration on how to implement this behavior.
+
+## Integrating the permission framework with your Backstage instance
+
+### 1. Set up the permission backend
+
+The permissions framework uses a new `permission-backend` plugin to accept authorization requests from other plugins across your Backstage instance. The Backstage backend does not include this permission backend by default, so you will need to add it:
+
+1. Add `@backstage/plugin-permission-backend` as a dependency of your Backstage backend:
+
+ ```bash title="From your Backstage root directory"
+ yarn --cwd packages/backend add @backstage/plugin-permission-backend
+ ```
+
+2. Add the following to a new file, `packages/backend/src/plugins/permission.ts`. This adds the permission-backend router, and configures it with a policy which allows everything.
+
+ ```typescript title="packages/backend/src/plugins/permission.ts"
+ import { createRouter } from '@backstage/plugin-permission-backend';
+ import {
+ AuthorizeResult,
+ PolicyDecision,
+ } from '@backstage/plugin-permission-common';
+ import { PermissionPolicy } from '@backstage/plugin-permission-node';
+ import { Router } from 'express';
+ import { PluginEnvironment } from '../types';
+
+ class TestPermissionPolicy implements PermissionPolicy {
+ async handle(): Promise {
+ return { result: AuthorizeResult.ALLOW };
+ }
+ }
+
+ export default async function createPlugin(
+ env: PluginEnvironment,
+ ): Promise {
+ return await createRouter({
+ config: env.config,
+ logger: env.logger,
+ discovery: env.discovery,
+ policy: new TestPermissionPolicy(),
+ identity: env.identity,
+ });
+ }
+ ```
+
+3. Wire up the permission policy in `packages/backend/src/index.ts`. [The index in the example backend](https://github.com/backstage/backstage/blob/master/packages/backend/src/index.ts) shows how to do this. You’ll need to import the module from the previous step, create a plugin environment, and add the router to the express app:
+
+ ```ts title="packages/backend/src/index.ts"
+ import proxy from './plugins/proxy';
+ import techdocs from './plugins/techdocs';
+ import search from './plugins/search';
+ /* highlight-add-next-line */
+ import permission from './plugins/permission';
+
+ async function main() {
+ const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs'));
+ const searchEnv = useHotMemoize(module, () => createEnv('search'));
+ const appEnv = useHotMemoize(module, () => createEnv('app'));
+ /* highlight-add-next-line */
+ const permissionEnv = useHotMemoize(module, () => createEnv('permission'));
+ // ..
+
+ apiRouter.use('/techdocs', await techdocs(techdocsEnv));
+ apiRouter.use('/proxy', await proxy(proxyEnv));
+ apiRouter.use('/search', await search(searchEnv));
+ /* highlight-add-next-line */
+ apiRouter.use('/permission', await permission(permissionEnv));
+ // ..
+ }
+ ```
+
+### 2. Enable and test the permissions system
+
+Now that the permission backend is running, it’s time to enable the permissions framework and make sure it’s working properly.
+
+1. Set the property `permission.enabled` to `true` in `app-config.yaml`.
+
+ ```yaml title="app-config.yaml"
+ permission:
+ enabled: true
+ ```
+
+2. Update the PermissionPolicy in `packages/backend/src/plugins/permission.ts` to disable a permission that’s easy for us to test. This policy rejects any attempt to delete a catalog entity:
+
+ ```ts title="packages/backend/src/plugins/permission.ts"
+ import { createRouter } from '@backstage/plugin-permission-backend';
+ import {
+ AuthorizeResult,
+ PolicyDecision,
+ } from '@backstage/plugin-permission-common';
+ /* highlight-remove-next-line */
+ import { PermissionPolicy } from '@backstage/plugin-permission-node';
+ /* highlight-add-start */
+ import {
+ PermissionPolicy,
+ PolicyQuery,
+ } from '@backstage/plugin-permission-node';
+ /* highlight-add-end */
+ import { Router } from 'express';
+ import { PluginEnvironment } from '../types';
+
+ class TestPermissionPolicy implements PermissionPolicy {
+ /* highlight-remove-next-line */
+ async handle(): Promise {
+ /* highlight-add-start */
+ async handle(request: PolicyQuery): Promise {
+ if (request.permission.name === 'catalog.entity.delete') {
+ return {
+ result: AuthorizeResult.DENY,
+ };
+ }
+ /* highlight-add-end */
+
+ return { result: AuthorizeResult.ALLOW };
+ }
+ }
+ ```
+
+3. Now that you’ve made this change, you should find that the unregister entity menu option on the catalog entity page is disabled.
+
+
+
+Now that the framework is fully configured, you can craft a permission policy that works best for your organization by utilizing a provided authorization method or by [writing your own policy](./writing-a-policy.md)!
diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md
index e8132e571b..d2c487e6ea 100644
--- a/docs/permissions/getting-started.md
+++ b/docs/permissions/getting-started.md
@@ -4,14 +4,8 @@ title: Getting Started
description: How to get started with the permission framework as an integrator
---
-If you prefer to watch a video instead, you can start with this video introduction:
-
-
-
-:::note Note
-
-This video was recorded in the January 2022 Contributors Session using `@backstage/create-app@0.4.14`. Some aspects of the demo may have changed in later releases.
-
+:::info
+This documentation is written for [the new backend system](../backend-system/index.md) which is the default since Backstage [version 1.24](../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./getting-started--old.md) instead, and [consider migrating](../backend-system/building-backends/08-migrating.md)!
:::
Backstage integrators control permissions by writing a policy. In general terms, a policy is simply an async function which receives a request to authorize a specific action for a user and (optional) resource, and returns a decision on whether to authorize that permission. Integrators can implement their own policies from scratch, or adopt reusable policies written by others.
@@ -22,13 +16,7 @@ The permissions framework depends on a few other Backstage systems, which must b
### Upgrade to the latest version of Backstage
-The permissions framework itself is new to Backstage and still evolving quickly. To ensure your version of Backstage has all the latest permission-related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade!
-
-### Enable service-to-service authentication
-
-Service-to-service authentication allows Backstage backend code to verify that a given request originates from elsewhere in the Backstage backend. This is useful for tasks like collation of catalog entities in the search index. This type of request shouldn’t be permissioned, so it’s important to configure this feature before trying to use the permissions framework.
-
-To set up service-to-service authentication, follow the [service-to-service authentication docs](../auth/service-to-service-auth.md).
+To ensure your version of Backstage has all the latest permission-related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade!
### Supply an identity resolver to populate group membership on sign in
@@ -38,33 +26,40 @@ Like many other parts of Backstage, the permissions framework relies on informat
[The IdentityResolver docs](../auth/identity-resolver.md) describe the process for resolving group membership on sign in.
-## Optionally add cookie-based authentication
+## Test Permission Policy
-Asset requests initiated by the browser will not include a token in the `Authorization` header. If these requests check authorization through the permission framework, as done in plugins like TechDocs, then you'll need to set up cookie-based authentication. Refer to the ["Authenticate API requests"](https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/authenticate-api-requests.md) tutorial for a demonstration on how to implement this behavior.
+To help validate the permission framework is setup we'll create a Test Permission Policy:
-## Integrating the permission framework with your Backstage instance
+1. Backstage ships with a default Allow All Policy, we want to remove that as it would override our Test Permission Policy. To do this remove the following line:
-### 1. Set up the permission backend
-
-The permissions framework uses a new `permission-backend` plugin to accept authorization requests from other plugins across your Backstage instance. The Backstage backend does not include this permission backend by default, so you will need to add it:
-
-1. Add `@backstage/plugin-permission-backend` as a dependency of your Backstage backend:
-
- ```bash title="From your Backstage root directory"
- yarn --cwd packages/backend add @backstage/plugin-permission-backend
+ ```ts title="packages/backend/src/index.ts"
+ // permission plugin
+ backend.add(import('@backstage/plugin-permission-backend/alpha'));
+ /* highlight-remove-start */
+ backend.add(
+ import('@backstage/plugin-permission-backend-module-allow-all-policy'),
+ );
+ /* highlight-remove-end */
```
-2. Add the following to a new file, `packages/backend/src/plugins/permission.ts`. This adds the permission-backend router, and configures it with a policy which allows everything.
+2. Now we need to add the `@backstage/backend-plugin-api` package:
- ```typescript title="packages/backend/src/plugins/permission.ts"
- import { createRouter } from '@backstage/plugin-permission-backend';
+ ```bash title="from your Backstage root directory"
+ yarn --cwd packages/backend add @backstage/backend-plugin-api
+ ```
+
+3. Next we will create an `extensions` folder under `packages/backend/src`
+4. In this new `extensions` folder we will add a new file called: `permissionsPolicyExtension.ts`
+5. Copy the following into the new `permissionsPolicyExtension.ts` file:
+
+ ```ts title="packages/backend/src/extensions/permissionsPolicyExtension.ts"
+ import { createBackendModule } from '@backstage/backend-plugin-api';
import {
- AuthorizeResult,
PolicyDecision,
+ AuthorizeResult,
} from '@backstage/plugin-permission-common';
import { PermissionPolicy } from '@backstage/plugin-permission-node';
- import { Router } from 'express';
- import { PluginEnvironment } from '../types';
+ import { policyExtensionPoint } from '@backstage/plugin-permission-node/alpha';
class TestPermissionPolicy implements PermissionPolicy {
async handle(): Promise {
@@ -72,48 +67,34 @@ The permissions framework uses a new `permission-backend` plugin to accept autho
}
}
- export default async function createPlugin(
- env: PluginEnvironment,
- ): Promise {
- return await createRouter({
- config: env.config,
- logger: env.logger,
- discovery: env.discovery,
- policy: new TestPermissionPolicy(),
- identity: env.identity,
- });
- }
+ export default createBackendModule({
+ pluginId: 'permission',
+ moduleId: 'permission-policy',
+ register(reg) {
+ reg.registerInit({
+ deps: { policy: policyExtensionPoint },
+ async init({ policy }) {
+ policy.setPolicy(new TestPermissionPolicy());
+ },
+ });
+ },
+ });
```
-3. Wire up the permission policy in `packages/backend/src/index.ts`. [The index in the example backend](https://github.com/backstage/backstage/blob/master/packages/backend/src/index.ts) shows how to do this. You’ll need to import the module from the previous step, create a plugin environment, and add the router to the express app:
+6. We now need to register this in the backend. We will do this by adding the follow line:
```ts title="packages/backend/src/index.ts"
- import proxy from './plugins/proxy';
- import techdocs from './plugins/techdocs';
- import search from './plugins/search';
+ // permission plugin
+ backend.add(import('@backstage/plugin-permission-backend/alpha'));
/* highlight-add-next-line */
- import permission from './plugins/permission';
-
- async function main() {
- const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs'));
- const searchEnv = useHotMemoize(module, () => createEnv('search'));
- const appEnv = useHotMemoize(module, () => createEnv('app'));
- /* highlight-add-next-line */
- const permissionEnv = useHotMemoize(module, () => createEnv('permission'));
- // ..
-
- apiRouter.use('/techdocs', await techdocs(techdocsEnv));
- apiRouter.use('/proxy', await proxy(proxyEnv));
- apiRouter.use('/search', await search(searchEnv));
- /* highlight-add-next-line */
- apiRouter.use('/permission', await permission(permissionEnv));
- // ..
- }
+ backend.add(import('./extensions/permissionPolicyExtension'));
```
-### 2. Enable and test the permissions system
+You now have a Test Permission Policy in place, this will help us test that the permission framework is working in the next section.
-Now that the permission backend is running, it’s time to enable the permissions framework and make sure it’s working properly.
+## Enable and test the permissions system
+
+Now lets test end to end that the permissions framework is setup and configured properly we will use the Test Permission Policy we create above as is, then modify it do deny access which will confirm everything is working as expected. Here's how to do that:
1. Set the property `permission.enabled` to `true` in `app-config.yaml`.
@@ -122,44 +103,11 @@ Now that the permission backend is running, it’s time to enable the permission
enabled: true
```
-2. Update the PermissionPolicy in `packages/backend/src/plugins/permission.ts` to disable a permission that’s easy for us to test. This policy rejects any attempt to delete a catalog entity:
+2. Now run `yarn dev`, Backstage should load up in your browser
+3. You should see that you have entities in your Catalog, pretty simple
+4. Let's change this line in our Test Permission Policy `return { result: AuthorizeResult.ALLOW };` to be `return { result: AuthorizeResult.DENY };`
+5. Run `yarn dev` once again, Backstage should load up in your browser
+6. This time you should not see any entities in your Catalog, if you do then something went wrong along the way and you'll need to review the steps above
+7. Revert the change we made in step 4 so that the line looks like this: `return { result: AuthorizeResult.ALLOW };`
- ```ts title="packages/backend/src/plugins/permission.ts"
- import { createRouter } from '@backstage/plugin-permission-backend';
- import {
- AuthorizeResult,
- PolicyDecision,
- } from '@backstage/plugin-permission-common';
- /* highlight-remove-next-line */
- import { PermissionPolicy } from '@backstage/plugin-permission-node';
- /* highlight-add-start */
- import {
- PermissionPolicy,
- PolicyQuery,
- } from '@backstage/plugin-permission-node';
- /* highlight-add-end */
- import { Router } from 'express';
- import { PluginEnvironment } from '../types';
-
- class TestPermissionPolicy implements PermissionPolicy {
- /* highlight-remove-next-line */
- async handle(): Promise {
- /* highlight-add-start */
- async handle(request: PolicyQuery): Promise {
- if (request.permission.name === 'catalog.entity.delete') {
- return {
- result: AuthorizeResult.DENY,
- };
- }
- /* highlight-add-end */
-
- return { result: AuthorizeResult.ALLOW };
- }
- }
- ```
-
-3. Now that you’ve made this change, you should find that the unregister entity menu option on the catalog entity page is disabled.
-
-
-
-Now that the framework is fully configured, you can craft a permission policy that works best for your organization by utilizing a provided authorization method or by [writing your own policy](./writing-a-policy.md)!
+Congratulations! Now that the framework is fully configured, you can craft a permission policy that works best for your organization by [writing your own policy](./writing-a-policy.md)!
diff --git a/docs/permissions/plugin-authors/05-frontend-authorization.md b/docs/permissions/plugin-authors/05-frontend-authorization.md
index 60458aaf4d..4d673e3b4f 100644
--- a/docs/permissions/plugin-authors/05-frontend-authorization.md
+++ b/docs/permissions/plugin-authors/05-frontend-authorization.md
@@ -199,7 +199,8 @@ const routes = (
- {/* highlight-add-end */}}
+ {/* highlight-add-end */}
+ }>
{/* ... */}
diff --git a/docs/permissions/writing-a-policy--old.md b/docs/permissions/writing-a-policy--old.md
new file mode 100644
index 0000000000..d95fa7ffb3
--- /dev/null
+++ b/docs/permissions/writing-a-policy--old.md
@@ -0,0 +1,148 @@
+---
+id: writing-a-policy--old
+title: Writing a permission policy
+description: How to write your own permission policy as a Backstage integrator
+---
+
+:::info
+This documentation is written for the old backend which has been replaced by [the new backend system](../backend-system/index.md), being the default since Backstage [version 1.24](../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./writing-a-policy.md) instead. Otherwise, [consider migrating](../backend-system/building-backends/08-migrating.md)!
+:::
+
+In the [previous section](./getting-started.md), we were able to set up the permission framework and make a simple change to our `TestPermissionPolicy` to confirm that policy is indeed wired up correctly.
+
+That policy looked like this:
+
+```typescript title="packages/backend/src/plugins/permission.ts"
+class TestPermissionPolicy implements PermissionPolicy {
+ async handle(
+ request: PolicyQuery,
+ _user?: PolicyQueryUser,
+ ): Promise {
+ if (request.permission.name === 'catalog.entity.delete') {
+ return {
+ result: AuthorizeResult.DENY,
+ };
+ }
+
+ return { result: AuthorizeResult.ALLOW };
+ }
+}
+```
+
+## What's in a policy?
+
+Let's break this down a bit further. The request object of type [PolicyQuery](https://backstage.io/docs/reference/plugin-permission-node.policyquery) is a simple wrapper around [the Permission object](https://backstage.io/docs/reference/plugin-permission-common.permission). This permission object encapsulates information about the action that the user is attempting to perform (See [the Concepts page](./concepts.md) for more details).
+
+In the policy above, we are checking to see if the provided action is a catalog entity delete action, which is the permission that the catalog plugin authors have created to represent the action of unregistering a catalog entity. If this is the case, we return a [Definitive Policy Decision](https://backstage.io/docs/reference/plugin-permission-common.definitivepolicydecision) of DENY. In all other cases, we return ALLOW (resulting in an allow-by-default behavior).
+
+As we confirmed in the previous section, we know that this now prevents us from unregistering catalog components. Hooray! But you may notice that this prevents _anyone_ from unregistering a component, which is not a very realistic policy. Let's improve this policy by disabling the unregister action _unless you are the owner of this component_.
+
+## Conditional decisions
+
+Let's change the policy to the following:
+
+```ts
+import {
+ AuthorizeResult,
+ PolicyDecision,
+ /* highlight-add-next-line */
+ isPermission,
+} from '@backstage/plugin-permission-common';
+/* highlight-add-start */
+import {
+ catalogConditions,
+ createCatalogConditionalDecision,
+} from '@backstage/plugin-catalog-backend/alpha';
+import {
+ catalogEntityDeletePermission,
+} from '@backstage/plugin-catalog-common/alpha';
+/* highlight-add-end */
+
+class TestPermissionPolicy implements PermissionPolicy {
+ /* highlight-remove-next-line */
+ async handle(request: PolicyQuery): Promise {
+ /* highlight-add-start */
+ async handle(
+ request: PolicyQuery,
+ user?: PolicyQueryUser,
+ ): Promise {
+ /* highlight-add-end */
+ /* highlight-remove-next-line */
+ if (request.permission.name === 'catalog.entity.delete') {
+ /* highlight-add-next-line */
+ if (isPermission(request.permission, catalogEntityDeletePermission)) {
+ /* highlight-remove-start */
+ return {
+ result: AuthorizeResult.DENY,
+ };
+ /* highlight-remove-end */
+ /* highlight-add-start */
+ return createCatalogConditionalDecision(
+ request.permission,
+ catalogConditions.isEntityOwner({
+ claims: user?.info.ownershipEntityRefs ?? [],
+ }),
+ );
+ /* highlight-add-end */
+ }
+ return { result: AuthorizeResult.ALLOW };
+ }
+}
+```
+
+Let's walk through the new code that we just added.
+
+Instead of returning an Definitive Policy Decision, we use factory methods to construct a [Conditional Policy Decision](https://backstage.io/docs/reference/plugin-permission-common.conditionalpolicydecision) (See the [Concepts page](./concepts.md) for more details). Since the policy doesn't have enough information to determine if `user` is the entity owner, this criteria is encapsulated within the conditional decision. However, `createCatalogConditionalDecision` will not compile unless `request.permission` is a catalog entity [`ResourcePermission`](https://backstage.io/docs/reference/plugin-permission-common.resourcepermission). This type constraint ensures that policies return conditional decisions that are compatible with the requested permission. To address this, we use [`isPermission`](https://backstage.io/docs/reference/plugin-permission-common.ispermission) to ["narrow"](https://www.typescriptlang.org/docs/handbook/2/narrowing.html) the type of `request.permission` to `ResourcePermission<'catalog-entity'>`. This matches the runtime behavior that was in place before, but you'll notice that the type of `request.permission` has changed within the scope of that `if` statement.
+
+The `catalogConditions` object contains all of the rules defined by the catalog plugin. These rules can be combined to form a [`PermissionCriteria`](https://backstage.io/docs/reference/plugin-permission-common.permissioncriteria) object, but for this case we only need to use the `isEntityOwner` rule. This rule accepts a list of entity refs that represent User identity and Group membership used to determine ownership. The second argument to `PermissionPolicy#handle` provides us with a `PolicyQueryUser` object, from which we can grab the user's `ownershipEntityRefs`. We provide an empty array as a fallback since the user may be anonymous.
+
+You should now be able to see in your Backstage app that the unregister entity button is enabled for entities that you own, but disabled for all other entities!
+
+## Resource types
+
+Now let's say we want to prevent all actions on catalog entities unless performed by the owner. One way to achieve this may be to simply update the `if` statement and check for each permission. If you choose to write your policy this way, it will certainly work! However, it may be difficult to maintain as the policy grows, and it may not be obvious if certain permissions are left out. We can author this same policy in a more scalable way by checking the resource type of the requested permission.
+
+```ts
+import {
+ AuthorizeResult,
+ PolicyDecision,
+ /* highlight-remove-next-line */
+ isPermission,
+ isResourcePermission,
+ /* highlight-add-next-line */
+} from '@backstage/plugin-permission-common';
+import {
+ catalogConditions,
+ createCatalogConditionalDecision,
+} from '@backstage/plugin-catalog-backend/alpha';
+/* highlight-remove-start */
+import {
+ catalogEntityDeletePermission,
+} from '@backstage/plugin-catalog-common/alpha';
+/* highlight-remove-end */
+
+class TestPermissionPolicy implements PermissionPolicy {
+ async handle(
+ request: PolicyQuery,
+ user?: PolicyQueryUser,
+ ): Promise {
+ /* highlight-remove-next-line */
+ if (isPermission(request.permission, catalogEntityDeletePermission)) {
+ /* highlight-add-next-line */
+ if (isResourcePermission(request.permission, 'catalog-entity')) {
+ return createCatalogConditionalDecision(
+ request.permission,
+ catalogConditions.isEntityOwner({
+ claims: user?.info.ownershipEntityRefs ?? [],
+ }),
+ );
+ }
+
+ return { result: AuthorizeResult.ALLOW };
+ }
+}
+```
+
+In this example, we use [`isResourcePermission`](https://backstage.io/docs/reference/plugin-permission-common.isresourcepermission) to match all permissions with a resource type of `catalog-entity`. Just like `isPermission`, this helper will "narrow" the type of `request.permission` and enable the use of `createCatalogConditionalDecision`. In addition to the behavior you observed before, you should also see that catalog entities are no longer visible unless you are the owner - success!
+
+_Note:_ Some catalog permissions do not have the `'catalog-entity'` resource type, such as [`catalogEntityCreatePermission`](https://github.com/backstage/backstage/blob/1e5e9fb9de9856a49e60fc70c38a4e4e94c69570/plugins/catalog-common/src/permissions.ts#L49). In those cases, a definitive decision is required because conditions can't be applied to an entity that does not exist yet.
diff --git a/docs/permissions/writing-a-policy.md b/docs/permissions/writing-a-policy.md
index 0f2d4fe091..ce662b6e1a 100644
--- a/docs/permissions/writing-a-policy.md
+++ b/docs/permissions/writing-a-policy.md
@@ -4,16 +4,40 @@ title: Writing a permission policy
description: How to write your own permission policy as a Backstage integrator
---
+:::info
+This documentation is written for [the new backend system](../backend-system/index.md) which is the default since Backstage [version 1.24](../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./writing-a-policy--old.md) instead, and [consider migrating](../backend-system/building-backends/08-migrating.md)!
+:::
+
In the [previous section](./getting-started.md), we were able to set up the permission framework and make a simple change to our `TestPermissionPolicy` to confirm that policy is indeed wired up correctly.
That policy looked like this:
-```typescript title="packages/backend/src/plugins/permission.ts"
+```ts title="packages/backend/src/extensions/permissionsPolicyExtension.ts"
class TestPermissionPolicy implements PermissionPolicy {
- async handle(
- request: PolicyQuery,
- _user?: PolicyQueryUser,
- ): Promise {
+ async handle(): Promise {
+ return { result: AuthorizeResult.ALLOW };
+ }
+}
+```
+
+That is a very simple example and it's not really doing anything helpful, let's expand this a little more.
+
+First, let's rename this from `TestPermissionPolicy` to `CustomPermissionPolicy` as you'll build on adding to it as your permissions needs require. Then we'll add a check for a permission. Here's what the full `permissionsPolicyExtension.ts` will look like:
+
+```ts title="packages/backend/src/extensions/permissionsPolicyExtension.ts"
+import { createBackendModule } from '@backstage/backend-plugin-api';
+import {
+ PolicyDecision,
+ AuthorizeResult,
+} from '@backstage/plugin-permission-common';
+import {
+ PermissionPolicy,
+ PolicyQuery,
+} from '@backstage/plugin-permission-node';
+import { policyExtensionPoint } from '@backstage/plugin-permission-node/alpha';
+
+class CustomPermissionPolicy implements PermissionPolicy {
+ async handle(request: PolicyQuery): Promise {
if (request.permission.name === 'catalog.entity.delete') {
return {
result: AuthorizeResult.DENY,
@@ -23,8 +47,23 @@ class TestPermissionPolicy implements PermissionPolicy {
return { result: AuthorizeResult.ALLOW };
}
}
+
+export default createBackendModule({
+ pluginId: 'permission',
+ moduleId: 'permission-policy',
+ register(reg) {
+ reg.registerInit({
+ deps: { policy: policyExtensionPoint },
+ async init({ policy }) {
+ policy.setPolicy(new CustomPermissionPolicy());
+ },
+ });
+ },
+});
```
+Now with this policy in place the ability to delete entities in the Catalog is not allowed for anyone. The following sections will expand on the concepts used here.
+
## What's in a policy?
Let's break this down a bit further. The request object of type [PolicyQuery](https://backstage.io/docs/reference/plugin-permission-node.policyquery) is a simple wrapper around [the Permission object](https://backstage.io/docs/reference/plugin-permission-common.permission). This permission object encapsulates information about the action that the user is attempting to perform (See [the Concepts page](./concepts.md) for more details).
@@ -54,7 +93,7 @@ import {
} from '@backstage/plugin-catalog-common/alpha';
/* highlight-add-end */
-class TestPermissionPolicy implements PermissionPolicy {
+class CustomPermissionPolicy implements PermissionPolicy {
/* highlight-remove-next-line */
async handle(request: PolicyQuery): Promise {
/* highlight-add-start */
@@ -117,7 +156,7 @@ import {
} from '@backstage/plugin-catalog-common/alpha';
/* highlight-remove-end */
-class TestPermissionPolicy implements PermissionPolicy {
+class CustomPermissionPolicy implements PermissionPolicy {
async handle(
request: PolicyQuery,
user?: PolicyQueryUser,
@@ -141,4 +180,8 @@ class TestPermissionPolicy implements PermissionPolicy {
In this example, we use [`isResourcePermission`](https://backstage.io/docs/reference/plugin-permission-common.isresourcepermission) to match all permissions with a resource type of `catalog-entity`. Just like `isPermission`, this helper will "narrow" the type of `request.permission` and enable the use of `createCatalogConditionalDecision`. In addition to the behavior you observed before, you should also see that catalog entities are no longer visible unless you are the owner - success!
-_Note:_ Some catalog permissions do not have the `'catalog-entity'` resource type, such as [`catalogEntityCreatePermission`](https://github.com/backstage/backstage/blob/1e5e9fb9de9856a49e60fc70c38a4e4e94c69570/plugins/catalog-common/src/permissions.ts#L49). In those cases, a definitive decision is required because conditions can't be applied to an entity that does not exist yet.
+:::note Note
+
+Some catalog permissions do not have the `'catalog-entity'` resource type, such as [`catalogEntityCreatePermission`](https://github.com/backstage/backstage/blob/1e5e9fb9de9856a49e60fc70c38a4e4e94c69570/plugins/catalog-common/src/permissions.ts#L49). In those cases, a definitive decision is required because conditions can't be applied to an entity that does not exist yet.
+
+:::
diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md
index 1a9c0babf9..a0e0e2d5e6 100644
--- a/docs/plugins/composability.md
+++ b/docs/plugins/composability.md
@@ -329,7 +329,7 @@ You can also use static configuration to bind routes, removing the need to make
changes to the app code. It does however mean that you won't get type safety
when binding routes and compile-time validation of the bindings. Static
configuration of route bindings is done under the `app.routes.bindings` key in
-`app-config.yaml`. It works the same way as [route bindings in the new frontend system](../frontend-system/architecture/07-routes.md#binding-external-route-references),
+`app-config.yaml`. It works the same way as [route bindings in the new frontend system](../frontend-system/architecture/36-routes.md#binding-external-route-references),
for example:
```yaml
@@ -342,7 +342,7 @@ app:
### Default Targets for External Route References
Following the `1.28` release of Backstage you can now define default targets for
-external route references. They work the same way as [default targets in the new frontend system](../frontend-system/architecture/07-routes.md#default-targets-for-external-route-references),
+external route references. They work the same way as [default targets in the new frontend system](../frontend-system/architecture/36-routes.md#default-targets-for-external-route-references),
for example:
```ts
diff --git a/docs/plugins/internationalization.md b/docs/plugins/internationalization.md
index 0f48e35a25..ba3f694d0c 100644
--- a/docs/plugins/internationalization.md
+++ b/docs/plugins/internationalization.md
@@ -151,32 +151,52 @@ export const myPluginTranslationRef = createTranslationRef({
Step 1: Create translation resources
+You should separate different translations to their own files and import them in the main file:
+
```ts
// packages/app/src/translations/userSettings.ts
import { createTranslationResource } from '@backstage/core-plugin-api/alpha';
import { userSettingsTranslationRef } from '@backstage/plugin-user-settings/alpha';
-export const userSettingsMessages = createTranslationResource({
+export const userSettingsTranslations = createTranslationResource({
ref: userSettingsTranslationRef,
translations: {
- zh: () =>
- Promise.resolve({
- default: {
- 'languageToggle.title': '语言',
- 'languageToggle.select': '选择{{language}}',
- 'languageToggle.description': '切换语言',
- 'themeToggle.title': '主题',
- 'themeToggle.description': '切换主题',
- 'themeToggle.select': '选择{{theme}}',
- 'themeToggle.selectAuto': '选择自动主题',
- 'themeToggle.names.auto': '自动',
- 'themeToggle.names.dark': '暗黑',
- 'themeToggle.names.light': '明亮',
- },
- }),
+ zh: () => import('./userSettings-zh'),
},
});
+
+// packages/app/src/translations/userSettings-zh.ts
+import { userSettingsTranslationRef } from '@backstage/plugin-user-settings/alpha';
+
+const zh = createTranslationMessages({
+ ref: userSettingsTranslationRef,
+ full: false, // False means that this is a partial translation
+ messages: {
+ 'languageToggle.title': '语言',
+ 'languageToggle.select': '选择{{language}}',
+ },
+});
+
+export default zh;
+```
+
+It's also possible to export the list of messages directly:
+
+```ts
+// packages/app/src/translations/userSettings-zh.ts
+export default {
+ 'languageToggle.title': '语言',
+ 'languageToggle.select': '选择{{language}}',
+ 'languageToggle.description': '切换语言',
+ 'themeToggle.title': '主题',
+ 'themeToggle.description': '切换主题',
+ 'themeToggle.select': '选择{{theme}}',
+ 'themeToggle.selectAuto': '选择自动主题',
+ 'themeToggle.names.auto': '自动',
+ 'themeToggle.names.dark': '暗黑',
+ 'themeToggle.names.light': '明亮',
+};
```
You should change `zh` under the translations object to your local language.
@@ -186,12 +206,12 @@ Step 2: Config translations in `packages/app/src/App.tsx`
In an app you can both override the default messages, as well as register translations for additional languages:
```diff
-+ import { userSettingsMessages } from './translations/userSettings';
++ import { userSettingsTranslations } from './translations/userSettings';
const app = createApp({
+ __experimentalTranslations: {
+ availableLanguages: ['en', 'zh'],
-+ resources: [userSettingsMessages],
++ resources: [userSettingsTranslations],
+ },
})
```
diff --git a/docs/plugins/observability.md b/docs/plugins/observability.md
index e13653706f..324e6751d4 100644
--- a/docs/plugins/observability.md
+++ b/docs/plugins/observability.md
@@ -17,7 +17,12 @@ See how to install Datadog Events in your app
### New Backend
-The backend supplies a central logging service, [`rootLogger`](../backend-system/core-services/root-logger.md), as well as a plugin based logger, [`logger`](../backend-system/core-services/logger.md) from `coreServices`. To add additional granularity to your logs, you can create children from the plugin based logger, using the `.child()` method and provide is with JSON data. For example, if you wanted to log items for a specific span in your plugin, you could do
+The backend supplies a central logging service,
+[`rootLogger`](../backend-system/core-services/root-logger.md), as well as a plugin
+based logger, [`logger`](../backend-system/core-services/logger.md) from `coreServices`.
+To add additional granularity to your logs, you can create children from the plugin
+based logger, using the `.child()` method and provide is with JSON data. For example,
+if you wanted to log items for a specific span in your plugin, you could do
```ts
export function createRouter({ logger }) {
@@ -37,7 +42,9 @@ export function createRouter({ logger }) {
}
```
-You can also add additional metadata to all logs for your Backstage instance by overriding the `rootLogger` implementation, you can see an example in [the `logger` docs](../backend-system/core-services/logger.md#configuring-the-service).
+You can also add additional metadata to all logs for your Backstage instance by
+overriding the `rootLogger` implementation, you can see an example in
+[the `rootLogger` docs](../backend-system/core-services/root-logger.md#configuring-the-service).
### Old Backend
@@ -63,9 +70,19 @@ An example log line could look as follows:
## Health Checks
-### New Backend
+### New Backend (post 1.29.0)
-The new backend is moving towards health checks being plugin-based, as such there is no current plugin for providing a health check route. You can add this yourself easily though,
+The new backend provides a `RootHealthService` which implements
+`/.backstage/health/v1/readiness` and `/.backstage/health/v1/liveness` endpoints
+to provide health checks for the entire backend instance.
+
+You can read more about this new service and how to customize it in the
+[Root Health Service documentation](../backend-system/core-services/root-health.md).
+
+### New Backend (pre 1.29.0)
+
+The new backend is moving towards health checks being plugin-based, as such there is no
+current plugin for providing a health check route. You can add this yourself easily though,
```ts
import {
diff --git a/docs/releases/v1.30.0-changelog.md b/docs/releases/v1.30.0-changelog.md
new file mode 100644
index 0000000000..0af531f483
--- /dev/null
+++ b/docs/releases/v1.30.0-changelog.md
@@ -0,0 +1,3093 @@
+# Release v1.30.0
+
+Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.30.0](https://backstage.github.io/upgrade-helper/?to=1.30.0)
+
+## @backstage/backend-app-api@0.9.0
+
+### Minor Changes
+
+- da4fde5: **BREAKING**: Removed several deprecated service factories. These can instead be imported from `@backstage/backend-defaults` package.
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+- 389f5a4: Remove deprecated `urlReaderServiceFactory`, please import from `@backstage/backend-defaults/urlReader` instead.
+
+### Patch Changes
+
+- 8b13183: Added support for the latest version of `BackendFeature`s from `@backstage/backend-plugin-api`, including feature loaders.
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- 7c5f3b0: Update the `ServiceRegister` implementation to enable registering multiple service implementations for a given service ref.
+- 80a0737: Added configuration for the `packages` options to config schema
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/config-loader@1.9.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/plugin-permission-node@0.8.1
+ - @backstage/cli-common@0.1.14
+ - @backstage/cli-node@0.2.7
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/backend-common@0.24.0
+
+### Minor Changes
+
+- 389f5a4: **BREAKING**: Removed the following `Url Reader` deprecated exports:
+
+ - UrlReader: Use `UrlReaderService` from `@backstage/backend-plugin-api` instead;
+ - AzureUrlReader: Import from `@backstage/backend-defaults/urlReader` instead;
+ - BitbucketUrlReader: Import from `@backstage/backend-defaults/urlReader` instead;
+ - BitbucketCloudUrlReader: Import from `@backstage/backend-defaults/urlReader` instead;
+ - BitbucketServerUrlReader: Import from `@backstage/backend-defaults/urlReader` instead;
+ - GithubUrlReader: Import from `@backstage/backend-defaults/urlReader` instead;
+ - GitlabUrlReader: Import from `@backstage/backend-defaults/urlReader` instead;
+ - GerritUrlReader: Import from `@backstage/backend-defaults/urlReader` instead;
+ - GiteaUrlReader: Import from `@backstage/backend-defaults/urlReader` instead;
+ - HarnessUrlReader: Import from `@backstage/backend-defaults/urlReader` instead;
+ - AwsS3UrlReader: Import from `@backstage/backend-defaults/urlReader` instead;
+ - FetchUrlReader: Import from `@backstage/backend-defaults/urlReader` instead;
+ - UrlReaders: Import from `@backstage/backend-defaults/urlReader` instead;
+ - UrlReadersOptions: Import from `@backstage/backend-defaults/urlReader` instead;
+ - UrlReaderPredicateTuple: Import from `@backstage/backend-defaults/urlReader` instead;
+ - FromReadableArrayOptions: Import from `@backstage/backend-defaults/urlReader` instead;
+ - ReaderFactory: Import from `@backstage/backend-defaults/urlReader` instead;
+ - ReadUrlOptions:Use `UrlReaderServiceReadUrlOptions` from `@backstage/backend-plugin-api` instead;
+ - ReadUrlResponse: Use `UrlReaderServiceReadUrlResponse` from `@backstage/backend-plugin-api` instead;
+ - ReadUrlResponseFactory: Import from `@backstage/backend-defaults/urlReader` instead;
+ - ReadUrlResponseFactoryFromStreamOptions: Import from `@backstage/backend-defaults/urlReader` instead;
+ - ReadTreeOptions: Use `UrlReaderServiceReadTreeOptions` from `@backstage/backend-plugin-api` instead;
+ - ReadTreeResponse: Use `UrlReaderServiceReadTreeResponse` from `@backstage/backend-plugin-api` instead;
+ - ReadTreeResponseFile: Use `UrlReaderServiceReadTreeResponseFile` from `@backstage/backend-plugin-api` instead;
+ - ReadTreeResponseDirOptions: Use `UrlReaderServiceReadTreeResponseDirOptions` from `@backstage/backend-plugin-api` instead;
+ - ReadTreeResponseFactory: Import from `@backstage/backend-defaults/urlReader` instead;
+ - ReadTreeResponseFactoryOptions: Import from `@backstage/backend-defaults/urlReader` instead;
+ - SearchOptions: Use `UrlReaderServiceSearchOptions` from `@backstage/backend-plugin-api` instead;
+ - SearchResponse: Use `UrlReaderServiceSearchResponse` from `@backstage/backend-plugin-api` instead;
+ - SearchResponseFile: Use `UrlReaderServiceSearchResponseFile` from `@backstage/backend-plugin-api` instead.
+
+### Patch Changes
+
+- ba8571e: Setup user agent header for AWS sdk clients, this enables users to better track API calls made from Backstage to AWS APIs through things like CloudTrail.
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+
+- 6795e33: This package is marked as `deprecated` and will be removed in a near future, please follow the deprecated instructions for the exports you still use.
+
+- 7e13b7a: The remaining exports in the package have now been deprecated:
+
+ - `cacheToPluginCacheManager`
+ - `createLegacyAuthAdapters`
+ - `LegacyCreateRouter`
+ - `legacyPlugin`
+ - `loggerToWinstonLogger`
+ - `makeLegacyPlugin`
+
+ Users of these export should fully [migrate to the new backend system](https://backstage.io/docs/backend-system/building-backends/migrating).
+
+- ddde5fe: Internal type refactor.
+
+- b63d378: export `createConfigSecretEnumerator` from `@backstage/backend-common` instead of `@backstage/backend-app-api`.
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/config-loader@1.9.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/backend-dev-utils@0.1.5
+ - @backstage/integration@1.14.0
+ - @backstage/cli-common@0.1.14
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/integration-aws-node@0.1.12
+ - @backstage/types@1.1.1
+
+## @backstage/backend-dynamic-feature-service@0.3.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 389f5a4: Update deprecated url-reader-related imports.
+- b63d378: Update internal imports
+- Updated dependencies
+ - @backstage/backend-defaults@0.4.2
+ - @backstage/backend-app-api@0.9.0
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-catalog-backend@1.25.0
+ - @backstage/plugin-search-backend-node@1.3.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/config-loader@1.9.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/plugin-permission-node@0.8.1
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/cli-common@0.1.14
+ - @backstage/cli-node@0.2.7
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-app-node@0.1.23
+ - @backstage/plugin-events-backend@0.3.10
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/backend-plugin-api@0.8.0
+
+### Minor Changes
+
+- 389f5a4: **BREAKING** Deleted the following deprecated `UrlReader` exports
+
+ - ReadUrlOptions: Use `UrlReaderServiceReadUrlOptions` instead;
+ - ReadUrlResponse: Use `UrlReaderServiceReadUrlResponse` instead;
+ - ReadTreeOptions: Use `UrlReaderServiceReadTreeOptions` instead;
+ - ReadTreeResponse: Use `UrlReaderServiceReadTreeResponse` instead;
+ - ReadTreeResponseFile: Use `UrlReaderServiceReadTreeResponseFile` instead;
+ - ReadTreeResponseDirOptions: Use `UrlReaderServiceReadTreeResponseDirOptions` instead;
+ - SearchOptions: Use `UrlReaderServiceSearchOptions` instead;
+ - SearchResponse: Use `UrlReaderServiceSearchResponse` instead;
+ - SearchResponseFile: Use `UrlReaderServiceSearchResponseFile` instead.
+
+- 7c5f3b0: The `createServiceRef` function now accepts a new boolean `multiple` option. The `multiple` option defaults to `false` and when set to `true`, it enables that multiple implementation are installed for the created service ref.
+
+ We're looking for ways to make it possible to augment services without the need to replace the entire service.
+
+ Typical example of that being the ability to install support for additional targets for the `UrlReader` service without replacing the service itself. This achieves that by allowing us to define services that can have multiple simultaneous implementation, allowing the `UrlReader` implementation to depend on such a service to collect all possible implementation of support for external targets:
+
+ ```diff
+ // @backstage/backend-defaults
+
+ + export const urlReaderFactoriesServiceRef = createServiceRef({
+ + id: 'core.urlReader.factories',
+ + scope: 'plugin',
+ + multiton: true,
+ + });
+
+ ...
+
+ export const urlReaderServiceFactory = createServiceFactory({
+ service: coreServices.urlReader,
+ deps: {
+ config: coreServices.rootConfig,
+ logger: coreServices.logger,
+ + factories: urlReaderFactoriesServiceRef,
+ },
+ - async factory({ config, logger }) {
+ + async factory({ config, logger, factories }) {
+ return UrlReaders.default({
+ config,
+ logger,
+ + factories,
+ });
+ },
+ });
+ ```
+
+ With that, you can then add more custom `UrlReader` factories by installing more implementations of the `urlReaderFactoriesServiceRef` in your backend instance. Something like:
+
+ ```ts
+ // packages/backend/index.ts
+ import { createServiceFactory } from '@backstage/backend-plugin-api';
+ import { urlReaderFactoriesServiceRef } from '@backstage/backend-defaults';
+ ...
+
+ backend.add(createServiceFactory({
+ service: urlReaderFactoriesServiceRef,
+ deps: {},
+ async factory() {
+ return CustomUrlReader.factory;
+ },
+ }));
+
+ ...
+
+ ```
+
+- c99c620: **BREAKING** Removed the following deprecated types:
+
+ - `ServiceRefConfig` use `ServiceRefOptions`
+ - `RootServiceFactoryConfig` use `RootServiceFactoryOptions`
+ - `PluginServiceFactoryConfig` use `PluginServiceFactoryOptions`
+
+### Patch Changes
+
+- 6061061: Added `createBackendFeatureLoader`, which can be used to create an installable backend feature that can in turn load in additional backend features in a dynamic way.
+
+- ba9abf4: The `SchedulerService` now allows tasks with `frequency: { trigger: 'manual' }`. This means that the task will not be scheduled, but rather run only when manually triggered with `SchedulerService.triggerTask`.
+
+- 8b13183: Added `createBackendFeatureLoader`, which can be used to programmatically select and install backend features.
+
+ A feature loader can return an list of features to be installed, for example in the form on an `Array` or other for of iterable, which allows for the loader to be defined as a generator function. Both synchronous and asynchronous loaders are supported.
+
+ Additionally, a loader can depend on services in its implementation, with the restriction that it can only depend on root-scoped services, and it may not override services that have already been instantiated.
+
+ ```ts
+ const searchLoader = createBackendFeatureLoader({
+ deps: {
+ config: coreServices.rootConfig,
+ },
+ *loader({ config }) {
+ // Example of a custom config flag to enable search
+ if (config.getOptionalString('customFeatureToggle.search')) {
+ yield import('@backstage/plugin-search-backend/alpha');
+ yield import('@backstage/plugin-search-backend-module-catalog/alpha');
+ yield import('@backstage/plugin-search-backend-module-explore/alpha');
+ yield import('@backstage/plugin-search-backend-module-techdocs/alpha');
+ }
+ },
+ });
+ ```
+
+- ddde5fe: Fixed a type issue where plugin and modules depending on multiton services would not receive the correct type.
+
+- f011d1b: fix typo in `getPluginRequestToken` comments
+
+- Updated dependencies
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/cli-common@0.1.14
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/backend-tasks@0.6.0
+
+### Minor Changes
+
+- fc24d9e: This package is deprecated and will be removed in a near future, follow the instructions below to stop using it:
+
+ - `TaskScheduler`: Please migrate to the new backend system, and depend on `coreServices.scheduler` from `@backstage/backend-plugin-api` instead, or use `DefaultSchedulerService` from \`@backstage/backend-defaults;
+ - `TaskRunner`: Please import `SchedulerServiceTaskRunner` from `@backstage/backend-plugin-api` instead;
+ - `TaskFunction`: Please import `SchedulerServiceTaskFunction` from `@backstage/backend-plugin-api` instead;
+ - `TaskDescriptor`: Please import `SchedulerServiceTaskDescriptor` from `@backstage/backend-plugin-api` instead;
+ - `TaskInvocationDefinition`: Please import `SchedulerServiceTaskInvocationDefinition` from `@backstage/backend-plugin-api` instead;
+ - `TaskScheduleDefinition`: Please import `SchedulerServiceTaskFunction` from `@backstage/backend-plugin-api` instead;
+ - `TaskScheduleDefinitionConfig`: Please import `SchedulerServiceTaskScheduleDefinitionConfig` from `@backstage/backend-plugin-api` instead;
+ - `PluginTaskScheduler`: Please use `SchedulerService` from `@backstage/backend-plugin-api` instead (most likely via `coreServices.scheduler`);
+ - `readTaskScheduleDefinitionFromConfig`: Please import `readSchedulerServiceTaskScheduleDefinitionFromConfig` from `@backstage/backend-plugin-api` instead;
+ - `HumanDuration`: Import `TypesHumanDuration` from `@backstage/types` instead.
+
+### Patch Changes
+
+- ba9abf4: The `PluginTaskScheduler` now allows tasks with `frequency: { trigger: 'manual' }`. This means that the task will not be scheduled, but rather run only when manually triggered with `PluginTaskScheduler.triggerTask`.
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/backend-test-utils@0.5.0
+
+### Minor Changes
+
+- 861f162: **BREAKING**: Removed these deprecated helpers:
+
+ - `setupRequestMockHandlers` is removed; use `registerMswTestHooks` instead.
+ - `MockDirectoryOptions` is removed; use `CreateMockDirectoryOptions` instead.
+
+ Stopped exporting the deprecated and internal `isDockerDisabledForTests` helper.
+
+ Removed `get` method from `ServiceFactoryTester` which is replaced by `getSubject`
+
+### Patch Changes
+
+- 8b13183: Internal updates to support latest version of `BackendFeauture`s from `@backstage/backend-plugin-api`.
+- b63d378: Update internal imports
+- 7c5f3b0: Update the `ServiceFactoryTester` to be able to test services that enables multi implementation installation.
+- 4e79d19: The default services for `startTestBackend` and `ServiceFactoryTester` now includes the Root Health Service.
+- Updated dependencies
+ - @backstage/backend-defaults@0.4.2
+ - @backstage/backend-app-api@0.9.0
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/catalog-model@1.6.0
+
+### Minor Changes
+
+- 34fa803: Introduce an optional spec.type attribute on the Domain and System entity kinds
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/cli@0.27.0
+
+### Minor Changes
+
+- 32a38e1: **BREAKING**: The lockfile (`yarn.lock`) dependency analysis and mutations have been removed from several commands.
+
+ The `versions:bump` command will no longer attempt to bump and deduplicate dependencies by modifying the lockfile, it will only update `package.json` files.
+
+ The `versions:check` command has been removed, since its only purpose was verification and mutation of the lockfile. We recommend using the `yarn dedupe` command instead, or the `yarn-deduplicate` package if you're using Yarn classic.
+
+ The check that was built into the `package start` command has been removed, it will no longer warn about lockfile mismatches.
+
+ The packages in the Backstage ecosystem handle package duplications much better now than when these CLI features were first introduced, so the need for these features has diminished. By removing them, we drastically reduce the integration between the Backstage CLI and Yarn, making it much easier to add support for other package managers in the future.
+
+### Patch Changes
+
+- 7eb08a6: Add frontend-dynamic-container role to eslint config factory
+- b2d97fd: Fixing loading of additional config files with new `ConfigSources`
+- fbc7819: Use ES2022 in CLI bundler
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- 6d898d8: Switched the `process` polyfill to use `require.resolve` for greater compatability.
+- e53074f: Updated default backend plugin to use `RootConfigService` instead of `Config`. This also removes the dependency on `@backstage/config` as it's no longer used.
+- ee2b0e5: The experimental module federation build now has the ability to force the use of development versions of `react` and `react-dom` by setting the `FORCE_REACT_DEVELOPMENT` flag.
+- 239dffc: Remove usage of deprecated functionality from @backstage/config-loader
+- e6e7d86: Switched the target from `'ES2022'` to `'es2022'` for better compatibility with older versions of `swc`.
+- 2ced236: Updated dependency `@module-federation/enhanced` to `0.3.1`
+- 0eedec3: Add support for dynamic plugins via the EXPERIMENTAL_MODULE_FEDERATION environment variable when running `yarn start`.
+- adabb40: New command now supports setting package license
+- dc4fb4f: Fix for `repo build --all` not properly detecting the experimental public entry point.
+- Updated dependencies
+ - @backstage/config-loader@1.9.0
+ - @backstage/integration@1.14.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/cli-common@0.1.14
+ - @backstage/cli-node@0.2.7
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/eslint-plugin@0.1.8
+ - @backstage/release-manifests@0.0.11
+ - @backstage/types@1.1.1
+
+## @backstage/config-loader@1.9.0
+
+### Minor Changes
+
+- 274428f: Add configuration key to File and Remote `ConfigSource`s that enables configuration of parsing logic. Previously limited to yaml, these `ConfigSource`s now allow for a multitude of parsing options (e.g. JSON).
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- 1edd6c2: The `env` option of `ConfigSources.default` now correctly allows undefined members.
+- 493feac: Add boolean `allowMissingDefaultConfig` option to `ConfigSources.default` and
+ `ConfigSources.defaultForTargets`, which results in omission of a ConfigSource
+ for the default app-config.yaml configuration file if it's not present.
+- Updated dependencies
+ - @backstage/cli-common@0.1.14
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/frontend-app-api@0.8.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 7777b5f: Support icon overriding with the new `IconBundleBlueprint` API.
+- 72754db: Updated usage of `useRouteRef`, which can now always return `undefined`.
+- 3be9aeb: Added support for v2 extensions, which declare their inputs and outputs without using a data map.
+- fe1fbb2: Migrating usages of the deprecated `createExtension` `v1` format to the newer `v2` format, and old `create*Extension` extension creators to blueprints.
+- 6349099: Added config input type to the extensions
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/core-components@0.14.10
+ - @backstage/core-app-api@1.14.2
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/theme@0.5.6
+ - @backstage/types@1.1.1
+ - @backstage/version-bridge@1.0.8
+
+## @backstage/frontend-plugin-api@0.7.0
+
+### Minor Changes
+
+- 72754db: **BREAKING**: All types of route refs are always considered optional by `useRouteRef`, which means the caller must always handle a potential `undefined` return value. Related to this change, the `optional` option from `createExternalRouteRef` has been removed, since it is no longer necessary.
+
+ This is released as an immediate breaking change as we expect the usage of the new route refs to be extremely low or zero, since plugins that support the new system will still use route refs and `useRouteRef` from `@backstage/core-plugin-api` in combination with `convertLegacyRouteRef` from `@backstage/core-compat-api`.
+
+### Patch Changes
+
+- 6f72c2b: Fixing issue with extension blueprints `inputs` merging.
+
+- 210d066: Added support for using the `params` in other properties of the `createExtensionBlueprint` options by providing a callback.
+
+- 9b356dc: Renamed `createPlugin` to `createFrontendPlugin`. The old symbol is still exported but deprecated.
+
+- a376559: Correct the `TConfig` type of data references to only contain config
+
+- 4e53ad6: Introduce a new way to encapsulate extension kinds that replaces the extension creator pattern with `createExtensionBlueprint`
+
+ This allows the creation of extension instances with the following pattern:
+
+ ```tsx
+ // create the extension blueprint which is used to create instances
+ const EntityCardBlueprint = createExtensionBlueprint({
+ kind: 'entity-card',
+ attachTo: { id: 'test', input: 'default' },
+ output: [coreExtensionData.reactElement],
+ factory(params: { text: string }) {
+ return [coreExtensionData.reactElement(
{params.text}
)];
+ },
+ });
+
+ // create an instance of the extension blueprint with params
+ const testExtension = EntityCardBlueprint.make({
+ name: 'foo',
+ params: {
+ text: 'Hello World',
+ },
+ });
+ ```
+
+- 9b89b82: The `ExtensionBoundary` now by default infers whether it's routable from whether it outputs a route path.
+
+- e493020: Deprecated `inputs` and `configSchema` options for `createComponentExtenion`, these will be removed in a future release
+
+- 7777b5f: Added a new `IconBundleBlueprint` that lets you create icon bundle extensions that can be installed in an App in order to override or add new app icons.
+
+ ```tsx
+ import { IconBundleBlueprint } from '@backstage/frontend-plugin-api';
+
+ const exampleIconBundle = IconBundleBlueprint.make({
+ name: 'example-bundle',
+ params: {
+ icons: {
+ user: MyOwnUserIcon,
+ },
+ },
+ });
+ ```
+
+- 99abb6b: Support overriding of plugin extensions using the new `plugin.withOverrides` method.
+
+ ```tsx
+ import homePlugin from '@backstage/plugin-home';
+
+ export default homePlugin.withOverrides({
+ extensions: [
+ homePage.getExtension('page:home').override({
+ *factory(originalFactory) {
+ yield* originalFactory();
+ yield coreExtensionData.reactElement(
My custom home page
);
+ },
+ }),
+ ],
+ });
+ ```
+
+- 813cac4: Add an `ExtensionBoundary.lazy` function to create properly wrapped lazy-loading enabled elements, suitable for use with `coreExtensionData.reactElement`. The page blueprint now automatically leverages this.
+
+- a65cfc8: Add support for accessing extensions definitions provided by a plugin via `plugin.getExtension(...)`. For this to work the extensions must be defined using the v2 format, typically using an extension blueprint.
+
+- 3be9aeb: Extensions have been changed to be declared with an array of inputs and outputs, rather than a map of named data refs. This change was made to reduce confusion around the role of the input and output names, as well as enable more powerful APIs for overriding extensions.
+
+ An extension that was previously declared like this:
+
+ ```tsx
+ const exampleExtension = createExtension({
+ name: 'example',
+ inputs: {
+ items: createExtensionInput({
+ element: coreExtensionData.reactElement,
+ }),
+ },
+ output: {
+ element: coreExtensionData.reactElement,
+ },
+ factory({ inputs }) {
+ return {
+ element: (
+
,
+ ),
+ ];
+ },
+ });
+ ```
+
+- 34f1b2a: Support merging of `inputs` in extension blueprints, but stop merging `output`. In addition, the original factory in extension blueprints now returns a data container that both provides access to the returned data, but can also be forwarded as output.
+
+- 3fb421d: Added support to be able to define `zod` config schema in Blueprints, with built in schema merging from the Blueprint and the extension instances.
+
+- 2d21599: Added support for being able to override extension definitions.
+
+ ```tsx
+ const TestCard = EntityCardBlueprint.make({
+ ...
+ });
+
+ TestCard.override({
+ // override attachment points
+ attachTo: { id: 'something-else', input: 'overridden' },
+ // extend the config schema
+ config: {
+ schema: {
+ newConfig: z => z.string().optional(),
+ }
+ },
+ // override factory
+ *factory(originalFactory, { inputs, config }){
+ const originalOutput = originalFactory();
+
+ yield coreExentsionData.reactElement(
+
+ {originalOutput.get(coreExentsionData.reactElement)}
+
+ );
+ }
+ });
+
+ ```
+
+- 31bfc44: Extension data references can now be defined in a way that encapsulates the ID string in the type, in addition to the data type itself. The old way of creating extension data references is deprecated and will be removed in a future release.
+
+ For example, the following code:
+
+ ```ts
+ export const myExtension =
+ createExtensionDataRef('my-plugin.my-data');
+ ```
+
+ Should be updated to the following:
+
+ ```ts
+ export const myExtension = createExtensionDataRef().with({
+ id: 'my-plugin.my-data',
+ });
+ ```
+
+- 6349099: Added config input type to the extensions
+
+- Updated dependencies
+ - @backstage/core-components@0.14.10
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/types@1.1.1
+ - @backstage/version-bridge@1.0.8
+
+## @backstage/integration@1.14.0
+
+### Minor Changes
+
+- 78c1329: Updated `GitlabUrlReader.readUrl` and `GitlabUrlReader.readTree` to accept a user-provided token, supporting both bearer and private tokens.
+
+### Patch Changes
+
+- c591670: Updated functions for `getHarnessEditContentsUrl`, `getHarnessFileContentsUrl`, `getHarnessArchiveUrl`, `getHarnessLatestCommitUrl` and `parseHarnessUrl` to handle account and org level urls
+- Updated dependencies
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.2.0
+
+### Minor Changes
+
+- 75d026a: Support for Cloudflare Custom Headers and Custom Cookie Auth Name
+
+### Patch Changes
+
+- c8f1cae: Add `signIn` to authentication provider configuration schema
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-auth-node@0.5.0
+
+### Minor Changes
+
+- 579afd0: **BREAKING**: Sign-in resolvers configured via `.signIn.resolvers` now take precedence over sign-in resolvers passed to `signInResolver` option of `createOAuthProviderFactory`. This effectively makes sign-in resolvers passed via the `signInResolver` the default one, which you can then override through configuration.
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-catalog@1.22.0
+
+### Minor Changes
+
+- 6925dcb: Introduces the HasSubdomainsCard component that displays the subdomains of a given domain
+
+### Patch Changes
+
+- 496b8a9: Export `RelatedEntitiesCard` presets to be reused.
+- 604a504: The entity relation cards available for the new frontend system via `/alpha` now have more accurate and granular default filters.
+- 7bd27e1: Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead.
+- c7603e8: Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead
+- 6582799: Add `tableOptions` to all tables and additionally `title` to API tables.
+- 6349099: Added config input type to the extensions
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/plugin-search-react@1.7.14
+ - @backstage/core-components@0.14.10
+ - @backstage/core-compat-api@0.2.8
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/integration-react@1.1.30
+ - @backstage/types@1.1.1
+ - @backstage/plugin-catalog-common@1.0.26
+ - @backstage/plugin-permission-react@0.4.25
+ - @backstage/plugin-scaffolder-common@1.5.5
+
+## @backstage/plugin-catalog-backend@1.25.0
+
+### Minor Changes
+
+- 163ba08: Deprecated `RouterOptions`, `CatalogBuilder`, and `CatalogEnvironment`. Please make sure to upgrade to the new backend system.
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 776eb56: `ProcessorOutputCollector` returns an error when receiving deferred entities that have an invalid `metadata.annotations` format.
+
+ This allows to return an error on an actual validation issue instead of reporting that the location annotations are missing afterwards, which is misleading for the users.
+
+- 389f5a4: Update deprecated url-reader-related imports.
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+
+- a629fb2: Added setAllowedLocationTypes while introducing a new extension point called CatalogLocationsExtensionPoint
+
+- 51240ee: Preserve default `allowedLocationTypes` when `setAllowedLocationTypes()` of `CatalogLocationsExtensionPoint` is not called.
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-permission-node@0.8.1
+ - @backstage/plugin-search-backend-module-catalog@0.2.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/integration@1.14.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/backend-openapi-utils@0.1.16
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-catalog-common@1.0.26
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-catalog-backend-module-aws@0.4.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- ba8571e: Setup user agent header for AWS sdk clients, this enables users to better track API calls made from Backstage to AWS APIs through things like CloudTrail.
+- 9342ac8: Removed unused dependency
+- 389f5a4: Update deprecated url-reader-related imports.
+- 90a7340: `AwsOrganizationCloudAccountProcessor` configuration field `roleArn` is deprecated in favor of new field `accountId`
+- Updated dependencies
+ - @backstage/backend-defaults@0.4.2
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/integration@1.14.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/integration-aws-node@0.1.12
+ - @backstage/plugin-catalog-common@1.0.26
+ - @backstage/plugin-kubernetes-common@0.8.2
+
+## @backstage/plugin-catalog-backend-module-azure@0.2.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 9342ac8: Removed unused dependency
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/integration@1.14.0
+ - @backstage/config@1.2.0
+ - @backstage/plugin-catalog-common@1.0.26
+
+## @backstage/plugin-catalog-backend-module-backstage-openapi@0.3.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/backend-openapi-utils@0.1.16
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.3.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/integration@1.14.0
+ - @backstage/plugin-bitbucket-cloud-common@0.2.22
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/plugin-catalog-common@1.0.26
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-catalog-backend-module-bitbucket-server@0.2.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 9342ac8: Removed unused dependency
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/integration@1.14.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-catalog-backend-module-gcp@0.2.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 9342ac8: Removed unused dependency
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/config@1.2.0
+ - @backstage/plugin-kubernetes-common@0.8.2
+
+## @backstage/plugin-catalog-backend-module-gerrit@0.2.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 9342ac8: Removed unused dependency
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/integration@1.14.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-catalog-backend-module-github@0.7.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+
+- c1eb809: Fix GitHub `repository` event support.
+
+ - `$.repository.organization` is only provided for `push` events. Switched to `$.organization.login` instead.
+ - `$.repository.url` is not always returning the expected and required value. Use `$.repository.html_url` instead.
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-catalog-backend@1.25.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/integration@1.14.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/plugin-catalog-common@1.0.26
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-catalog-backend-module-github-org@0.2.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 9342ac8: Removed unused dependency
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-catalog-backend-module-github@0.7.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/config@1.2.0
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-catalog-backend-module-gitlab@0.4.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- c7b14ed: Adds new optional `excludeRepos` configuration option to the Gitlab catalog provider.
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/integration@1.14.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/config@1.2.0
+ - @backstage/plugin-catalog-common@1.0.26
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-catalog-backend-module-gitlab-org@0.1.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-catalog-backend-module-gitlab@0.4.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.5.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 389f5a4: Update deprecated url-reader-related imports.
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-catalog-backend@1.25.0
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-catalog-backend-module-ldap@0.8.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-catalog-common@1.0.26
+
+## @backstage/plugin-catalog-backend-module-msgraph@0.6.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 9342ac8: Removed unused dependency
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- 58dff4d: Added option to ingest groups based on their group membership in Azure Entra ID
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/config@1.2.0
+ - @backstage/plugin-catalog-common@1.0.26
+
+## @backstage/plugin-catalog-backend-module-puppetdb@0.2.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 9342ac8: Removed unused dependency
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-events-backend-module-aws-sqs@0.4.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- ba8571e: Setup user agent header for AWS sdk clients, this enables users to better track API calls made from Backstage to AWS APIs through things like CloudTrail.
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/config@1.2.0
+ - @backstage/types@1.1.1
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-notifications@0.3.0
+
+### Minor Changes
+
+- 0410fc9: By default, set notification as read when opening snackbar or web notification link
+
+### Patch Changes
+
+- 80b84f7: Fixed issue with notification reloading on page change
+- b58e452: Broadcast notifications are now decorated with an icon.
+- Updated dependencies
+ - @backstage/core-components@0.14.10
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/theme@0.5.6
+ - @backstage/types@1.1.1
+ - @backstage/plugin-notifications-common@0.0.5
+ - @backstage/plugin-signals-react@0.0.4
+
+## @backstage/plugin-notifications-backend-module-email@0.2.0
+
+### Minor Changes
+
+- def53a7: **BREAKING** Following `NotificationTemplateRenderer` methods now return a Promise and **must** be awaited: `getSubject`, `getText` and `getHtml`.
+
+ Required changes and example usage:
+
+ ```diff
+ import { notificationsEmailTemplateExtensionPoint } from '@backstage/plugin-notifications-backend-module-email';
+ import { Notification } from '@backstage/plugin-notifications-common';
+ +import { getNotificationSubject, getNotificationTextContent, getNotificationHtmlContent } from 'my-notification-processing-library`
+ export const notificationsModuleEmailDecorator = createBackendModule({
+ pluginId: 'notifications',
+ moduleId: 'email.templates',
+ register(reg) {
+ reg.registerInit({
+ deps: {
+ emailTemplates: notificationsEmailTemplateExtensionPoint,
+ },
+ async init({ emailTemplates }) {
+ emailTemplates.setTemplateRenderer({
+ - getSubject(notification) {
+ + async getSubject(notification) {
+ - return `New notification from ${notification.source}`;
+ + const subject = await getNotificationSubject(notification);
+ + return `New notification from ${subject}`;
+ },
+ - getText(notification) {
+ + async getText(notification) {
+ - return notification.content;
+ + const text = await getNotificationTextContent(notification);
+ + return text;
+ },
+ - getHtml(notification) {
+ + async getHtml(notification) {
+ - return `
${notification.content}
`;
+ + const html = await getNotificationHtmlContent(notification);
+ + return html;
+ },
+ });
+ },
+ });
+ },
+ });
+ ```
+
+### Patch Changes
+
+- d55b8e3: Avoid sending broadcast emails as a fallback in case the entity-typed notification user can not be resolved.
+- cdb630d: Add support for stream transport for debugging purposes
+- 83faf24: Notification email processor supports allowing or denying specific email addresses from receiving notifications
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-notifications-node@0.2.4
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/integration-aws-node@0.1.12
+ - @backstage/types@1.1.1
+ - @backstage/plugin-notifications-common@0.0.5
+
+## @backstage/plugin-scaffolder@1.24.0
+
+### Minor Changes
+
+- 1552c33: Changed the way to display entities in `MyGroupsPicker` to use `entityPresentationApi` and make it consistent across scaffolder pickers
+- 3fca643: Added field extension `RepoBranchPicker` that supports autocompletion for Bitbucket
+
+### Patch Changes
+
+- 47ed51b: Add an extra bit of height to the EntityPicker dropdown to make it clear there are more options to select from, and to remove the scroll bar when there is less than 10 options
+- 46e5e55: Change scaffolder widgets to use `TextField` component for more flexibility in theme overrides.
+- c7603e8: Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead
+- 2ae63cd: add i18n for scaffolder
+- d18f4eb: Fix undefined in the title of Scaffolder Runs on the page load
+- 896a22d: Fix helper text margin for scaffolder EntityNamePicker and EntityTagsPicker when using outlined text field
+- bbd9f56: Cleaned up codebase of RepoUrlPicker
+- b8600fe: Fix issue with `RepoUrlPicker` not refreshing the credentials for a different host
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/plugin-scaffolder-react@1.11.0
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/core-components@0.14.10
+ - @backstage/core-compat-api@0.2.8
+ - @backstage/integration@1.14.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/integration-react@1.1.30
+ - @backstage/types@1.1.1
+ - @backstage/plugin-catalog-common@1.0.26
+ - @backstage/plugin-permission-react@0.4.25
+ - @backstage/plugin-scaffolder-common@1.5.5
+
+## @backstage/plugin-scaffolder-backend@1.24.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+- dcd6a79: Added OpenTelemetry support to Scaffolder metrics
+
+### Patch Changes
+
+- 389f5a4: Update deprecated url-reader-related imports.
+- c544f81: Add support for status filtering in scaffolder tasks endpoint
+- b63d378: Update internal imports
+- ef87e06: Fix scaffolder action `catalog:write` to write to directories that don't already exist
+- Updated dependencies
+ - @backstage/backend-defaults@0.4.2
+ - @backstage/plugin-scaffolder-backend-module-github@0.4.1
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.13
+ - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.13
+ - @backstage/plugin-scaffolder-backend-module-bitbucket@0.2.13
+ - @backstage/plugin-scaffolder-backend-module-gerrit@0.1.15
+ - @backstage/plugin-scaffolder-backend-module-gitea@0.1.13
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/plugin-scaffolder-backend-module-azure@0.1.15
+ - @backstage/plugin-permission-node@0.8.1
+ - @backstage/plugin-scaffolder-backend-module-gitlab@0.4.5
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/integration@1.14.0
+ - @backstage/plugin-bitbucket-cloud-common@0.2.22
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.21
+ - @backstage/plugin-scaffolder-common@1.5.5
+
+## @backstage/plugin-scaffolder-react@1.11.0
+
+### Minor Changes
+
+- 8839381: Add scaffolder option to display object items in separate rows on review page
+
+### Patch Changes
+
+- 072c00c: Fixed a bug in `DefaultTableOutputs` where output elements overlapped on smaller screen sizes
+- 46e5e55: Change scaffolder widgets to use `TextField` component for more flexibility in theme overrides.
+- d0e95a7: Add ability to customise form fields in the UI by exposing `uiSchema` and `formContext` in `FormProps`
+- 4670f06: support `ajv-errors` for scaffolder validation to allow for customizing the error messages
+- 04759f2: Fix null check in `isJsonObject` utility function for scaffolder review state component
+- Updated dependencies
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/core-components@0.14.10
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/theme@0.5.6
+ - @backstage/types@1.1.1
+ - @backstage/version-bridge@1.0.8
+ - @backstage/plugin-permission-react@0.4.25
+ - @backstage/plugin-scaffolder-common@1.5.5
+
+## @backstage/plugin-search-backend-module-catalog@0.2.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-search-backend-node@1.3.0
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/plugin-catalog-common@1.0.26
+
+## @backstage/plugin-search-backend-module-explore@0.2.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- 93fc1a0: Updated dependency `@backstage-community/plugin-explore-common` to `^0.0.4`.
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-search-backend-node@1.3.0
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/config@1.2.0
+
+## @backstage/plugin-search-backend-module-stack-overflow-collator@0.2.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-search-backend-node@1.3.0
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/config@1.2.0
+
+## @backstage/plugin-search-backend-module-techdocs@0.2.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-search-backend-node@1.3.0
+ - @backstage/plugin-techdocs-node@1.12.9
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/plugin-catalog-common@1.0.26
+
+## @backstage/plugin-search-backend-node@1.3.0
+
+### Minor Changes
+
+- fc24d9e: Stop using `@backstage/backend-tasks` as it will be deleted in near future.
+
+### Patch Changes
+
+- 389f5a4: Update deprecated url-reader-related imports.
+- 3123c16: Fix package metadata
+- 7c5f3b0: Explicit declare if the service ref accepts `single` or `multiple` implementations.
+- Updated dependencies
+ - @backstage/backend-defaults@0.4.2
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-techdocs-common@0.1.0
+
+### Minor Changes
+
+- 4698e1f: Initial release of the techdocs-common package.
+
+## @backstage/app-defaults@1.5.10
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/core-components@0.14.10
+ - @backstage/core-app-api@1.14.2
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/theme@0.5.6
+ - @backstage/plugin-permission-react@0.4.25
+
+## @backstage/backend-defaults@0.4.2
+
+### Patch Changes
+
+- 0d16b52: Add access restrictions to the JWKS external access method config schema
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- 3b429fb: Added deprecation warning to urge users to perform the auth service migration or implement their own token manager service.
+ See for more information.
+- 7681b17: update the `morgan` middleware to use a custom format to prevent PII from being logged
+- 4e79d19: The `createHealthRouter` utility that allows you to create a health check router is now exported via `@backstage/backend-defaults/rootHttpRouter`.
+- ba9abf4: The `SchedulerService` now allows tasks with `frequency: { trigger: 'manual' }`. This means that the task will not be scheduled, but rather run only when manually triggered with `SchedulerService.triggerTask`.
+- 78c1329: Updated `GitlabUrlReader.readUrl` and `GitlabUrlReader.readTree` to accept a user-provided token, supporting both bearer and private tokens.
+- 8e967da: Fixed the routing of the new health check service, the health endpoints should now properly be available at `/.backstage/health/v1/readiness` and `/.backstage/health/v1/liveness`.
+- 7c5f3b0: Update the `UrlReader` service to depends on multiple instances of `UrlReaderFactoryProvider` service.
+- 81f930a: use formatted query to prevent chance of SQL-injection
+- 1d5f298: Avoid excessive numbers of error listeners on cache clients
+- Updated dependencies
+ - @backstage/backend-app-api@0.9.0
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/config-loader@1.9.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/plugin-permission-node@0.8.1
+ - @backstage/backend-dev-utils@0.1.5
+ - @backstage/integration@1.14.0
+ - @backstage/cli-common@0.1.14
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/integration-aws-node@0.1.12
+ - @backstage/types@1.1.1
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/backend-dev-utils@0.1.5
+
+### Patch Changes
+
+- 3a35172: Fix `EventEmitter` memory leak in the development utilities
+
+## @backstage/backend-openapi-utils@0.1.16
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/errors@1.2.4
+
+## @backstage/catalog-client@1.6.6
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/catalog-model@1.6.0
+ - @backstage/errors@1.2.4
+
+## @backstage/core-app-api@1.14.2
+
+### Patch Changes
+
+- 9a46a81: The request to delete the session cookie when running the app in protected mode is now done with a plain `fetch` rather than `FetchApi`. This fixes a bug where the app would immediately try to sign-in again when removing the cookie during logout.
+- Updated dependencies
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/types@1.1.1
+ - @backstage/version-bridge@1.0.8
+
+## @backstage/core-compat-api@0.2.8
+
+### Patch Changes
+
+- 72754db: Updated usage of `useRouteRef`, which can now always return `undefined`.
+- fe1fbb2: Migrating usages of the deprecated `createExtension` `v1` format to the newer `v2` format, and old `create*Extension` extension creators to blueprints.
+- 16cf96c: Both `compatWrapper` and `convertLegacyRouteRef` now support converting from the new system to the old.
+- 519b8e0: Added new utilities for converting legacy plugins and extensions to the new system. The `convertLegacyPlugin` option will convert an existing plugin to the new system, although you need to supply extensions for the plugin yourself. To help out with this, there is also a new `convertLegacyPageExtension` which converts an existing page extension to the new system.
+- 6349099: Added config input type to the extensions
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/version-bridge@1.0.8
+
+## @backstage/core-components@0.14.10
+
+### Patch Changes
+
+- 678971a: Move the `Link` component to the `RoutedTabs` instead of the `HeaderTabs` component
+- 13a9c63: Corrected the documentation for the GCP IAP auth module and updated the configuration to follow proxy configuration conventions by ignoring authEnv
+- Updated dependencies
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/theme@0.5.6
+ - @backstage/version-bridge@1.0.8
+
+## @backstage/create-app@0.5.18
+
+### Patch Changes
+
+- c0a705d: Added the Kubernetes plugin to `create-app`
+- d7a0aa3: Bumped create-app version.
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- 6c1081c: Updated dockerfile and `app-config.production.yaml` to make it easier to get started with example data
+- bfeba46: Included permission config and enabled it out of the box
+- Updated dependencies
+ - @backstage/cli-common@0.1.14
+
+## @backstage/dev-utils@1.0.37
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/core-components@0.14.10
+ - @backstage/core-app-api@1.14.2
+ - @backstage/catalog-model@1.6.0
+ - @backstage/app-defaults@1.5.10
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/integration-react@1.1.30
+ - @backstage/theme@0.5.6
+
+## @backstage/frontend-test-utils@0.1.12
+
+### Patch Changes
+
+- 8209449: Added new APIs for testing extensions
+
+- 72754db: Updated usage of `useRouteRef`, which can now always return `undefined`.
+
+- 3be9aeb: Added support for v2 extensions, which declare their inputs and outputs without using a data map.
+
+- fe1fbb2: Migrating usages of the deprecated `createExtension` `v1` format to the newer `v2` format, and old `create*Extension` extension creators to blueprints.
+
+- 2d21599: Added support for being able to override extension definitions.
+
+ ```tsx
+ const TestCard = EntityCardBlueprint.make({
+ ...
+ });
+
+ TestCard.override({
+ // override attachment points
+ attachTo: { id: 'something-else', input: 'overridden' },
+ // extend the config schema
+ config: {
+ schema: {
+ newConfig: z => z.string().optional(),
+ }
+ },
+ // override factory
+ *factory(originalFactory, { inputs, config }){
+ const originalOutput = originalFactory();
+
+ yield coreExentsionData.reactElement(
+
+ {originalOutput.get(coreExentsionData.reactElement)}
+
+ );
+ }
+ });
+
+ ```
+
+- c00e1a0: Deprecate the `.render` method of the `createExtensionTester` in favour of using `renderInTestApp` directly.
+
+ ```tsx
+ import {
+ renderInTestApp,
+ createExtensionTester,
+ } from '@backstage/frontend-test-utils';
+
+ const tester = createExtensionTester(extension);
+
+ const { getByTestId } = renderInTestApp(tester.reactElement());
+
+ // or if you're not using `coreExtensionData.reactElement` as the output ref
+ const { getByTestId } = renderInTestApp(tester.get(myComponentRef));
+ ```
+
+- 264e10f: Deprecate existing `ExtensionCreators` in favour of their new Blueprint counterparts.
+
+- 264e10f: Refactor `.make` method on Blueprints into two different methods, `.make` and `.makeWithOverrides`.
+
+ When using `createExtensionBlueprint` you can define parameters for the factory function, if you wish to take advantage of these parameters you should use `.make` when creating an extension instance of a Blueprint. If you wish to override more things other than the standard `attachTo`, `name`, `namespace` then you should use `.makeWithOverrides` instead.
+
+ `.make` is reserved for simple creation of extension instances from Blueprints using higher level parameters, whereas `.makeWithOverrides` is lower level and you have more control over the final extension.
+
+- 6349099: Added config input type to the extensions
+
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/frontend-app-api@0.8.0
+ - @backstage/config@1.2.0
+ - @backstage/test-utils@1.5.10
+ - @backstage/types@1.1.1
+
+## @backstage/integration-react@1.1.30
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/integration@1.14.0
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+
+## @backstage/repo-tools@0.9.5
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/config-loader@1.9.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/cli-common@0.1.14
+ - @backstage/cli-node@0.2.7
+ - @backstage/errors@1.2.4
+
+## @techdocs/cli@1.8.17
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-defaults@0.4.2
+ - @backstage/plugin-techdocs-node@1.12.9
+ - @backstage/catalog-model@1.6.0
+ - @backstage/cli-common@0.1.14
+ - @backstage/config@1.2.0
+
+## @backstage/test-utils@1.5.10
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/core-app-api@1.14.2
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/theme@0.5.6
+ - @backstage/types@1.1.1
+ - @backstage/plugin-permission-react@0.4.25
+
+## @backstage/plugin-api-docs@0.11.8
+
+### Patch Changes
+
+- 770ba02: `ConsumingComponentsCard` and `ProvidingComponentsCard` will now optionally accept `columns` to override which table columns are displayed
+- fe1fbb2: Migrating usages of the deprecated `createExtension` `v1` format to the newer `v2` format, and old `create*Extension` extension creators to blueprints.
+- ebfeb40: Added `resolvers` prop to `AsyncApiDefinitionWidget`. This allows to override the default http/https resolvers, for example to add authentication to requests to internal schema registries.
+- 4b6d2cb: Updated dependency `@graphiql/react` to `^0.23.0`.
+- 6582799: Add `tableOptions` to all tables and additionally `title` to API tables.
+- Updated dependencies
+ - @backstage/plugin-catalog@1.22.0
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/core-components@0.14.10
+ - @backstage/core-compat-api@0.2.8
+ - @backstage/catalog-model@1.6.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/plugin-catalog-common@1.0.26
+ - @backstage/plugin-permission-react@0.4.25
+
+## @backstage/plugin-app-backend@0.3.72
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- 6bd6fda: Deprecate `createRouter` and its options in favour of the new backend system.
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/config-loader@1.9.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-app-node@0.1.23
+
+## @backstage/plugin-app-node@0.1.23
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/config-loader@1.9.0
+
+## @backstage/plugin-app-visualizer@0.1.9
+
+### Patch Changes
+
+- 72754db: Updated usage of `useRouteRef`, which can now always return `undefined`.
+- c7603e8: Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead
+- e493020: Fixing issue with the visualizer crashing when clicking on the detailed view because of `routeRef` parameters
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/core-components@0.14.10
+ - @backstage/core-plugin-api@1.9.3
+
+## @backstage/plugin-auth-backend@0.22.10
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- cc9a7a5: Deprecated `createRouter` and its router options in favour of the new backend system.
+- Updated dependencies
+ - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.2.0
+ - @backstage/plugin-auth-backend-module-atlassian-provider@0.2.4
+ - @backstage/plugin-auth-backend-module-bitbucket-provider@0.1.6
+ - @backstage/plugin-auth-backend-module-microsoft-provider@0.1.18
+ - @backstage/plugin-auth-backend-module-onelogin-provider@0.1.4
+ - @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.15
+ - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.18
+ - @backstage/plugin-auth-backend-module-github-provider@0.1.20
+ - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.20
+ - @backstage/plugin-auth-backend-module-google-provider@0.1.20
+ - @backstage/plugin-auth-backend-module-oauth2-provider@0.2.4
+ - @backstage/plugin-auth-backend-module-oidc-provider@0.2.4
+ - @backstage/plugin-auth-backend-module-okta-provider@0.0.16
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.1.6
+ - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.16
+
+## @backstage/plugin-auth-backend-module-atlassian-provider@0.2.4
+
+### Patch Changes
+
+- c8f1cae: Add `signIn` to authentication provider configuration schema
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+
+## @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.15
+
+### Patch Changes
+
+- c8f1cae: Add `signIn` to authentication provider configuration schema
+
+- 4ea354f: Added a `signer` configuration option to validate against the token claims. We strongly recommend that you set this value (typically on the format `arn:aws:elasticloadbalancing:us-east-2:123456789012:loadbalancer/app/my-load-balancer/1234567890123456`) to ensure that the auth provider can safely check the authenticity of any incoming tokens.
+
+ Example:
+
+ ```diff
+ auth:
+ providers:
+ awsalb:
+ # this is the URL of the IdP you configured
+ issuer: 'https://example.okta.com/oauth2/default'
+ # this is the ARN of your ALB instance
+ + signer: 'arn:aws:elasticloadbalancing:us-east-2:123456789012:loadbalancer/app/my-load-balancer/1234567890123456'
+ # this is the region where your ALB instance resides
+ region: 'us-west-2'
+ signIn:
+ resolvers:
+ # typically you would pick one of these
+ - resolver: emailMatchingUserEntityProfileEmail
+ - resolver: emailLocalPartMatchingUserEntityName
+ ```
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-auth-backend@0.22.10
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.1.6
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-auth-backend-module-bitbucket-provider@0.1.6
+
+### Patch Changes
+
+- c8f1cae: Add `signIn` to authentication provider configuration schema
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+
+## @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.18
+
+### Patch Changes
+
+- c8f1cae: Add `signIn` to authentication provider configuration schema
+- 13a9c63: Corrected the documentation for the GCP IAP auth module and updated the configuration to follow proxy configuration conventions by ignoring authEnv
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-auth-backend-module-github-provider@0.1.20
+
+### Patch Changes
+
+- c8f1cae: Add `signIn` to authentication provider configuration schema
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+
+## @backstage/plugin-auth-backend-module-gitlab-provider@0.1.20
+
+### Patch Changes
+
+- c8f1cae: Add `signIn` to authentication provider configuration schema
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+
+## @backstage/plugin-auth-backend-module-google-provider@0.1.20
+
+### Patch Changes
+
+- c8f1cae: Add `signIn` to authentication provider configuration schema
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+
+## @backstage/plugin-auth-backend-module-guest-provider@0.1.9
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-auth-backend-module-microsoft-provider@0.1.18
+
+### Patch Changes
+
+- c8f1cae: Add `signIn` to authentication provider configuration schema
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- 39f36a9: Updated the Microsoft authenticator to accurately define required scopes, but to also omit the required and additional scopes when requesting resource scopes.
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+
+## @backstage/plugin-auth-backend-module-oauth2-provider@0.2.4
+
+### Patch Changes
+
+- c8f1cae: Add `signIn` to authentication provider configuration schema
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+
+## @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.16
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-auth-backend-module-oidc-provider@0.2.4
+
+### Patch Changes
+
+- c8f1cae: Add `signIn` to authentication provider configuration schema
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-auth-backend@0.22.10
+ - @backstage/plugin-auth-node@0.5.0
+
+## @backstage/plugin-auth-backend-module-okta-provider@0.0.16
+
+### Patch Changes
+
+- c8f1cae: Add `signIn` to authentication provider configuration schema
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+
+## @backstage/plugin-auth-backend-module-onelogin-provider@0.1.4
+
+### Patch Changes
+
+- c8f1cae: Add `signIn` to authentication provider configuration schema
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+
+## @backstage/plugin-auth-backend-module-pinniped-provider@0.1.17
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/config@1.2.0
+
+## @backstage/plugin-auth-backend-module-vmware-cloud-provider@0.2.4
+
+### Patch Changes
+
+- c8f1cae: Add `signIn` to authentication provider configuration schema
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/catalog-model@1.6.0
+
+## @backstage/plugin-auth-react@0.1.5
+
+### Patch Changes
+
+- aeac3e9: feat: Hide visibility of CookieAuthRedirect
+- Updated dependencies
+ - @backstage/core-components@0.14.10
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-bitbucket-cloud-common@0.2.22
+
+### Patch Changes
+
+- 3fca643: Added method `listBranchesByRepository` to `BitbucketCloudClient`
+- Updated dependencies
+ - @backstage/integration@1.14.0
+
+## @backstage/plugin-catalog-backend-module-logs@0.0.2
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-catalog-backend@1.25.0
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-catalog-backend-module-openapi@0.1.41
+
+### Patch Changes
+
+- 389f5a4: Update deprecated url-reader-related imports.
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-catalog-backend@1.25.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/integration@1.14.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/config@1.2.0
+ - @backstage/types@1.1.1
+ - @backstage/plugin-catalog-common@1.0.26
+
+## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.21
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/plugin-catalog-common@1.0.26
+ - @backstage/plugin-scaffolder-common@1.5.5
+
+## @backstage/plugin-catalog-backend-module-unprocessed@0.4.10
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/errors@1.2.4
+ - @backstage/plugin-catalog-unprocessed-entities-common@0.0.4
+
+## @backstage/plugin-catalog-common@1.0.26
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/catalog-model@1.6.0
+
+## @backstage/plugin-catalog-graph@0.4.8
+
+### Patch Changes
+
+- c7603e8: Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead
+- fba7537: Memoize entity graph nodes when applying an `entityFilter` to prevent repeated redraws
+- 4a529c2: Use `entityPresentationApi` for the node title and the icon.
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/core-components@0.14.10
+ - @backstage/core-compat-api@0.2.8
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-catalog-import@0.12.2
+
+### Patch Changes
+
+- c7603e8: Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/core-components@0.14.10
+ - @backstage/core-compat-api@0.2.8
+ - @backstage/integration@1.14.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/integration-react@1.1.30
+ - @backstage/plugin-catalog-common@1.0.26
+
+## @backstage/plugin-catalog-node@1.12.5
+
+### Patch Changes
+
+- a629fb2: Added setAllowedLocationTypes while introducing a new extension point called CatalogLocationsExtensionPoint
+- 7c5f3b0: Explicit declare if the service ref accepts `single` or `multiple` implementations.
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-permission-node@0.8.1
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-catalog-common@1.0.26
+
+## @backstage/plugin-catalog-react@1.12.3
+
+### Patch Changes
+
+- 7bd27e1: Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead.
+- 31bfc44: Updated alpha definitions of extension data references.
+- 7ca331c: Correct `EntityDisplayName`'s icon alignment with the text.
+- 9b89b82: Internal refactor to remove unnecessary `routable` prop in the implementation of the `createEntityContentExtension` alpha export.
+- bebd569: Fix extra divider displayed on user list picker component
+- 519b8e0: Added utilities for converting existing entity card and content extensions to the new frontend system. This is in particular useful when used in combination with the new `convertLegacyPlugin` utility from `@backstage/core-compat-api`.
+- d001a42: Fix label related accessibility issues with `FavorityEntity`
+- 012e3eb: Entity page extensions created for the new frontend system via the `/alpha` exports will now be enabled by default.
+- 6349099: Added config input type to the extensions
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/core-components@0.14.10
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/core-compat-api@0.2.8
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/integration-react@1.1.30
+ - @backstage/types@1.1.1
+ - @backstage/version-bridge@1.0.8
+ - @backstage/plugin-catalog-common@1.0.26
+ - @backstage/plugin-permission-react@0.4.25
+
+## @backstage/plugin-catalog-unprocessed-entities@0.2.7
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/core-components@0.14.10
+ - @backstage/catalog-model@1.6.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-catalog-unprocessed-entities-common@0.0.4
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-permission-common@0.8.1
+
+## @backstage/plugin-config-schema@0.1.58
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/core-components@0.14.10
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-devtools@0.1.17
+
+### Patch Changes
+
+- c7603e8: Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/core-components@0.14.10
+ - @backstage/core-compat-api@0.2.8
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/plugin-devtools-common@0.1.12
+ - @backstage/plugin-permission-react@0.4.25
+
+## @backstage/plugin-devtools-backend@0.3.9
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- 32a38e1: Removed unused code for lockfile analysis.
+- 2886ef7: Deprecated `createRouter` and its router options in favour of the new backend system.
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/config-loader@1.9.0
+ - @backstage/plugin-permission-node@0.8.1
+ - @backstage/cli-common@0.1.14
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-devtools-common@0.1.12
+
+## @backstage/plugin-devtools-common@0.1.12
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-events-backend@0.3.10
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/config@1.2.0
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-events-backend-module-azure@0.2.9
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-events-backend-module-bitbucket-cloud@0.2.9
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-events-backend-module-gerrit@0.2.9
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-events-backend-module-github@0.2.9
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/config@1.2.0
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-events-backend-module-gitlab@0.2.9
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/config@1.2.0
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-events-backend-test-utils@0.1.33
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-events-node@0.3.9
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+
+## @backstage/plugin-home@0.7.9
+
+### Patch Changes
+
+- 31bfc44: Updated alpha definitions of extension data references.
+- fe1fbb2: Migrating usages of the deprecated `createExtension` `v1` format to the newer `v2` format, and old `create*Extension` extension creators to blueprints.
+- fdcc059: Fixed a bug on the WelcomeTitle component where the welcome message wasn't correct when the language was set to Spanish
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/core-components@0.14.10
+ - @backstage/core-compat-api@0.2.8
+ - @backstage/core-app-api@1.14.2
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/theme@0.5.6
+ - @backstage/plugin-home-react@0.1.16
+
+## @backstage/plugin-home-react@0.1.16
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/core-components@0.14.10
+ - @backstage/core-plugin-api@1.9.3
+
+## @backstage/plugin-kubernetes@0.11.13
+
+### Patch Changes
+
+- e6c15cc: Adds support for Backstage's new frontend system, available via the `/alpha` sub-path export.
+- fe1fbb2: Migrating usages of the deprecated `createExtension` `v1` format to the newer `v2` format, and old `create*Extension` extension creators to blueprints.
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/plugin-kubernetes-react@0.4.2
+ - @backstage/core-components@0.14.10
+ - @backstage/core-compat-api@0.2.8
+ - @backstage/catalog-model@1.6.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/plugin-kubernetes-common@0.8.2
+
+## @backstage/plugin-kubernetes-backend@0.18.4
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- b63d378: Update internal imports
+- 8c1aa06: Add `kubernetes.clusterLocatorMethods[].clusters[].customResources` to the configuration schema.
+ This was already documented and supported by the plugin.
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-kubernetes-node@0.1.17
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/plugin-permission-node@0.8.1
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/integration-aws-node@0.1.12
+ - @backstage/types@1.1.1
+ - @backstage/plugin-kubernetes-common@0.8.2
+
+## @backstage/plugin-kubernetes-cluster@0.0.14
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/plugin-kubernetes-react@0.4.2
+ - @backstage/core-components@0.14.10
+ - @backstage/catalog-model@1.6.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/plugin-kubernetes-common@0.8.2
+
+## @backstage/plugin-kubernetes-common@0.8.2
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/catalog-model@1.6.0
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-kubernetes-node@0.1.17
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- b63d378: Update internal imports
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/types@1.1.1
+ - @backstage/plugin-kubernetes-common@0.8.2
+
+## @backstage/plugin-kubernetes-react@0.4.2
+
+### Patch Changes
+
+- 954a593: `Liveness Probe` added in ContainerCard Component of PodDrawer
+- Updated dependencies
+ - @backstage/core-components@0.14.10
+ - @backstage/catalog-model@1.6.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-kubernetes-common@0.8.2
+
+## @backstage/plugin-notifications-backend@0.3.4
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- d55b8e3: Avoid sending broadcast emails as a fallback in case the entity-typed notification user can not be resolved.
+- 8013044: fix: consider broadcast union with user
+- 7a05f50: Allow using notifications without users in the catalog
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-notifications-node@0.2.4
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/plugin-events-node@0.3.9
+ - @backstage/plugin-notifications-common@0.0.5
+ - @backstage/plugin-signals-node@0.1.9
+
+## @backstage/plugin-notifications-node@0.2.4
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/plugin-notifications-common@0.0.5
+ - @backstage/plugin-signals-node@0.1.9
+
+## @backstage/plugin-org@0.6.28
+
+### Patch Changes
+
+- c7603e8: Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/core-components@0.14.10
+ - @backstage/core-compat-api@0.2.8
+ - @backstage/catalog-model@1.6.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/plugin-catalog-common@1.0.26
+
+## @backstage/plugin-org-react@0.1.27
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/core-components@0.14.10
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/core-plugin-api@1.9.3
+
+## @backstage/plugin-permission-backend@0.5.47
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/plugin-permission-node@0.8.1
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-permission-backend-module-allow-all-policy@0.1.20
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/plugin-permission-node@0.8.1
+
+## @backstage/plugin-permission-common@0.8.1
+
+### Patch Changes
+
+- df784fe: Add the MetadataResponse type from @backstage/plugin-permission-node, since this
+ type might be used in frontend code.
+- 137fa34: Add the MetadataResponseSerializedRule type from @backstage/plugin-permission-node, since this type might be used in frontend code.
+- Updated dependencies
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-permission-node@0.8.1
+
+### Patch Changes
+
+- df784fe: The MetadataResponse type has been moved to @backstage/plugin-permission-common
+ to match the recent move of MetadataResponseSerializedRule, and should be
+ imported from there going forward. To avoid an immediate breaking change, this
+ type is still re-exported from this package, but is marked as deprecated and
+ will be removed in a future release.
+- 5cd9878: The MetadataResponseSerializedRule type has been moved to @backstage/plugin-permission-common, and should be imported from there going forward. To avoid an immediate breaking change, this type is still re-exported from this package, but is marked as deprecated and will be removed in a future release.
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-permission-react@0.4.25
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+
+## @backstage/plugin-proxy-backend@0.5.4
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- b63d378: Update internal imports
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/config@1.2.0
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-scaffolder-backend-module-azure@0.1.15
+
+### Patch Changes
+
+- 187f583: Added examples for publish:azure action and updated its test cases
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/integration@1.14.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-scaffolder-backend-module-bitbucket@0.2.13
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.13
+ - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.13
+ - @backstage/integration@1.14.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.13
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- 3fca643: Added autocompletion support for resource `branches`
+- d57967c: Add ability to set the initial commit message when initializing a repository using the scaffolder action.
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/integration@1.14.0
+ - @backstage/plugin-bitbucket-cloud-common@0.2.22
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.13
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- e3b64be: Added examples for publish:bitbucketServer action and improve its test cases
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/integration@1.14.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.24
+
+### Patch Changes
+
+- 389f5a4: Update deprecated url-reader-related imports.
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/integration@1.14.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.47
+
+### Patch Changes
+
+- 389f5a4: Update deprecated url-reader-related imports.
+- dae85df: Add examples for `fetch:cookiecutter` scaffolder action & improve related tests
+- Updated dependencies
+ - @backstage/backend-defaults@0.4.2
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/integration@1.14.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-scaffolder-backend-module-gcp@0.1.1
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/integration@1.14.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-scaffolder-backend-module-gerrit@0.1.15
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/integration@1.14.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-scaffolder-backend-module-gitea@0.1.13
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- 24de005: Added test cases for publish:gitea examples
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/integration@1.14.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-scaffolder-backend-module-github@0.4.1
+
+### Patch Changes
+
+- d21d307: Added examples for github:environment:create action and improve its test cases
+- 6d4cb97: Added examples for github:repo:create action and improved test cases
+- cd203f1: Added examples for action github:pages and improved its test cases
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/integration@1.14.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-scaffolder-backend-module-gitlab@0.4.5
+
+### Patch Changes
+
+- da97131: Added test cases for gitlab:issues:create examples
+
+- fad1b90: Allow the `createGitlabProjectVariableAction` to use oauth tokens
+
+- aab708e: Added test cases for gitlab:issue:edit examples
+
+- ef742dc: Added test cases for gitlab:projectAccessToken:create example
+
+- 1ba4c2f: Added test cases for gitlab:pipeline:trigger examples
+
+- a6603e4: Add custom action for merge request: **auto**
+
+ The **Auto** action selects the committed action between _create_ and _update_.
+
+ The **Auto** action fetches files using the **/projects/repository/tree endpoint**.
+ After fetching, it checks if the file exists locally and in the repository. If it does, it chooses **update**; otherwise, it chooses **create**.
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/integration@1.14.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-scaffolder-backend-module-notifications@0.0.6
+
+### Patch Changes
+
+- 6fc03c7: Add examples for notification:send scaffolder action & improve related tests
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/plugin-notifications-node@0.2.4
+ - @backstage/plugin-notifications-common@0.0.5
+
+## @backstage/plugin-scaffolder-backend-module-rails@0.4.40
+
+### Patch Changes
+
+- 389f5a4: Update deprecated url-reader-related imports.
+- 449def7: Add examples for fetch:rails scaffolder action & improve related tests
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/integration@1.14.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-scaffolder-backend-module-sentry@0.1.31
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- 382e868: Added test cases for sentry:project:create examples
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-scaffolder-backend-module-yeoman@0.3.7
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/types@1.1.1
+ - @backstage/plugin-scaffolder-node-test-utils@0.1.10
+
+## @backstage/plugin-scaffolder-common@1.5.5
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/catalog-model@1.6.0
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-scaffolder-node@0.4.9
+
+### Patch Changes
+
+- 389f5a4: Update deprecated url-reader-related imports.
+- c544f81: Add support for status filtering in scaffolder tasks endpoint
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/integration@1.14.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-scaffolder-common@1.5.5
+
+## @backstage/plugin-scaffolder-node-test-utils@0.1.10
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-common@0.24.0
+ - @backstage/backend-test-utils@0.5.0
+ - @backstage/plugin-scaffolder-node@0.4.9
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-search@1.4.15
+
+### Patch Changes
+
+- c7603e8: Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead
+- 3123c16: Fix package metadata
+- 6349099: Added config input type to the extensions
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/plugin-search-react@1.7.14
+ - @backstage/core-components@0.14.10
+ - @backstage/core-compat-api@0.2.8
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/version-bridge@1.0.8
+
+## @backstage/plugin-search-backend@1.5.15
+
+### Patch Changes
+
+- 3123c16: Fix package metadata
+- Updated dependencies
+ - @backstage/backend-defaults@0.4.2
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-search-backend-node@1.3.0
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-permission-node@0.8.1
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/backend-openapi-utils@0.1.16
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-search-backend-module-elasticsearch@1.5.4
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-search-backend-node@1.3.0
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/config@1.2.0
+ - @backstage/integration-aws-node@0.1.12
+
+## @backstage/plugin-search-backend-module-pg@0.5.33
+
+### Patch Changes
+
+- 7251567: Removing `@backstage/backend-app-api` dependency
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-search-backend-node@1.3.0
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/config@1.2.0
+
+## @backstage/plugin-search-common@1.2.14
+
+### Patch Changes
+
+- 3123c16: Fix package metadata
+- Updated dependencies
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/types@1.1.1
+
+## @backstage/plugin-search-react@1.7.14
+
+### Patch Changes
+
+- 7bd27e1: Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead.
+- 31bfc44: Updated alpha definitions of extension data references.
+- 3123c16: Fix package metadata
+- 6349099: Added config input type to the extensions
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/core-components@0.14.10
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/theme@0.5.6
+ - @backstage/types@1.1.1
+ - @backstage/version-bridge@1.0.8
+
+## @backstage/plugin-signals@0.0.9
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/core-components@0.14.10
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/theme@0.5.6
+ - @backstage/types@1.1.1
+ - @backstage/plugin-signals-react@0.0.4
+
+## @backstage/plugin-signals-backend@0.1.9
+
+### Patch Changes
+
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/config@1.2.0
+ - @backstage/types@1.1.1
+ - @backstage/plugin-events-node@0.3.9
+ - @backstage/plugin-signals-node@0.1.9
+
+## @backstage/plugin-signals-node@0.1.9
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/config@1.2.0
+ - @backstage/types@1.1.1
+ - @backstage/plugin-events-node@0.3.9
+
+## @backstage/plugin-techdocs@1.10.8
+
+### Patch Changes
+
+- 69bd940: Use annotation constants from new techdocs-common package.
+- c7603e8: Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead
+- 27794d1: Allow for more granular control of TechDocsReaderPage styling. Theme overrides can now be provided to TechDocs without affecting the theme in other areas of Backstage.
+- 4490d73: Refactor TechDocs' mkdocs-redirects support.
+- 8543e72: TechDocs redirect feature now includes a notification to the user before they are redirected.
+- 67e76f2: TechDocs now supports the `mkdocs-redirects` plugin. Redirects defined using the `mkdocs-redirect` plugin will be handled automatically in TechDocs. Redirecting to external urls is not supported. In the case that an external redirect url is provided, TechDocs will redirect to the current documentation site home.
+- bdc5471: Fixed issue where header styles were incorrectly generated when themes used CSS variables to define font size.
+- 6349099: Added config input type to the extensions
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/plugin-search-react@1.7.14
+ - @backstage/core-components@0.14.10
+ - @backstage/core-compat-api@0.2.8
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/integration@1.14.0
+ - @backstage/plugin-techdocs-common@0.1.0
+ - @backstage/plugin-auth-react@0.1.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/integration-react@1.1.30
+ - @backstage/theme@0.5.6
+ - @backstage/plugin-techdocs-react@1.2.7
+
+## @backstage/plugin-techdocs-addons-test-utils@1.0.37
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-catalog@1.22.0
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/plugin-search-react@1.7.14
+ - @backstage/plugin-techdocs@1.10.8
+ - @backstage/core-app-api@1.14.2
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/integration-react@1.1.30
+ - @backstage/test-utils@1.5.10
+ - @backstage/plugin-techdocs-react@1.2.7
+
+## @backstage/plugin-techdocs-backend@1.10.10
+
+### Patch Changes
+
+- 69bd940: Use annotation constants from new techdocs-common package.
+- 93095ee: Make sure node-fetch is version 2.7.0 or greater
+- b77fbf4: Added back `type: 'local'` to TechDocs config schema for `publisher`
+- a16632c: Update configuration schema to match actual behavior
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-techdocs-node@1.12.9
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-search-backend-module-techdocs@0.2.0
+ - @backstage/integration@1.14.0
+ - @backstage/plugin-techdocs-common@0.1.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/plugin-catalog-common@1.0.26
+
+## @backstage/plugin-techdocs-module-addons-contrib@1.1.13
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/core-components@0.14.10
+ - @backstage/integration@1.14.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/integration-react@1.1.30
+ - @backstage/plugin-techdocs-react@1.2.7
+
+## @backstage/plugin-techdocs-node@1.12.9
+
+### Patch Changes
+
+- 389f5a4: Update deprecated url-reader-related imports.
+- 69bd940: Use annotation constants from new techdocs-common package.
+- 949083d: Update `patchMkdocsYmlPrebuild` to modify `repo_url` and `edit_uri` independently.
+- 5cedd9f: Fix TechDocs Edit URL for nested docs
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/integration@1.14.0
+ - @backstage/plugin-techdocs-common@0.1.0
+ - @backstage/catalog-model@1.6.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/integration-aws-node@0.1.12
+
+## @backstage/plugin-techdocs-react@1.2.7
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/core-components@0.14.10
+ - @backstage/catalog-model@1.6.0
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/version-bridge@1.0.8
+
+## @backstage/plugin-user-settings@0.8.11
+
+### Patch Changes
+
+- c7603e8: Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead
+- 6349099: Added config input type to the extensions
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/core-components@0.14.10
+ - @backstage/core-compat-api@0.2.8
+ - @backstage/core-app-api@1.14.2
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/theme@0.5.6
+ - @backstage/types@1.1.1
+ - @backstage/plugin-signals-react@0.0.4
+ - @backstage/plugin-user-settings-common@0.0.1
+
+## @backstage/plugin-user-settings-backend@0.2.22
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-signals-node@0.1.9
+ - @backstage/plugin-user-settings-common@0.0.1
+
+## example-app@0.2.100
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-catalog@1.22.0
+ - @backstage/plugin-scaffolder@1.24.0
+ - @backstage/plugin-scaffolder-react@1.11.0
+ - @backstage/cli@0.27.0
+ - @backstage/plugin-notifications@0.3.0
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/plugin-search-react@1.7.14
+ - @backstage/plugin-home@0.7.9
+ - @backstage/plugin-techdocs@1.10.8
+ - @backstage/core-components@0.14.10
+ - @backstage/plugin-api-docs@0.11.8
+ - @backstage/frontend-app-api@0.8.0
+ - @backstage/plugin-catalog-graph@0.4.8
+ - @backstage/plugin-catalog-import@0.12.2
+ - @backstage/plugin-devtools@0.1.17
+ - @backstage/plugin-org@0.6.28
+ - @backstage/plugin-search@1.4.15
+ - @backstage/plugin-user-settings@0.8.11
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/plugin-kubernetes@0.11.13
+ - @backstage/core-app-api@1.14.2
+ - @backstage/plugin-auth-react@0.1.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/app-defaults@1.5.10
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/integration-react@1.1.30
+ - @backstage/theme@0.5.6
+ - @backstage/plugin-catalog-common@1.0.26
+ - @backstage/plugin-catalog-unprocessed-entities@0.2.7
+ - @backstage/plugin-kubernetes-cluster@0.0.14
+ - @backstage/plugin-permission-react@0.4.25
+ - @backstage/plugin-signals@0.0.9
+ - @backstage/plugin-techdocs-module-addons-contrib@1.1.13
+ - @backstage/plugin-techdocs-react@1.2.7
+
+## example-app-next@0.0.14
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-catalog@1.22.0
+ - @backstage/plugin-scaffolder@1.24.0
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/plugin-scaffolder-react@1.11.0
+ - @backstage/cli@0.27.0
+ - @backstage/plugin-notifications@0.3.0
+ - @backstage/plugin-catalog-react@1.12.3
+ - @backstage/plugin-search-react@1.7.14
+ - @backstage/plugin-home@0.7.9
+ - @backstage/plugin-techdocs@1.10.8
+ - @backstage/core-components@0.14.10
+ - @backstage/plugin-api-docs@0.11.8
+ - @backstage/frontend-app-api@0.8.0
+ - @backstage/core-compat-api@0.2.8
+ - @backstage/plugin-app-visualizer@0.1.9
+ - @backstage/plugin-catalog-graph@0.4.8
+ - @backstage/plugin-catalog-import@0.12.2
+ - @backstage/plugin-org@0.6.28
+ - @backstage/plugin-search@1.4.15
+ - @backstage/plugin-user-settings@0.8.11
+ - @backstage/plugin-search-common@1.2.14
+ - @backstage/plugin-kubernetes@0.11.13
+ - @backstage/core-app-api@1.14.2
+ - @backstage/plugin-auth-react@0.1.5
+ - @backstage/catalog-model@1.6.0
+ - @backstage/app-defaults@1.5.10
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/integration-react@1.1.30
+ - @backstage/theme@0.5.6
+ - @backstage/plugin-catalog-common@1.0.26
+ - @backstage/plugin-catalog-unprocessed-entities@0.2.7
+ - @backstage/plugin-kubernetes-cluster@0.0.14
+ - @backstage/plugin-permission-react@0.4.25
+ - @backstage/plugin-signals@0.0.9
+ - @backstage/plugin-techdocs-module-addons-contrib@1.1.13
+ - @backstage/plugin-techdocs-react@1.2.7
+
+## app-next-example-plugin@0.0.14
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.7.0
+ - @backstage/core-components@0.14.10
+
+## example-backend@0.0.29
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-defaults@0.4.2
+ - @backstage/plugin-scaffolder-backend-module-github@0.4.1
+ - @backstage/plugin-auth-backend-module-github-provider@0.1.20
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/plugin-catalog-backend@1.25.0
+ - @backstage/plugin-catalog-backend-module-openapi@0.1.41
+ - @backstage/plugin-search-backend-node@1.3.0
+ - @backstage/plugin-scaffolder-backend@1.24.0
+ - @backstage/plugin-techdocs-backend@1.10.10
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-search-backend-module-techdocs@0.2.0
+ - @backstage/plugin-search-backend-module-explore@0.2.0
+ - @backstage/plugin-notifications-backend@0.3.4
+ - @backstage/plugin-kubernetes-backend@0.18.4
+ - @backstage/plugin-permission-backend@0.5.47
+ - @backstage/plugin-devtools-backend@0.3.9
+ - @backstage/plugin-signals-backend@0.1.9
+ - @backstage/plugin-proxy-backend@0.5.4
+ - @backstage/plugin-auth-backend@0.22.10
+ - @backstage/plugin-app-backend@0.3.72
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/plugin-permission-node@0.8.1
+ - @backstage/plugin-catalog-backend-module-backstage-openapi@0.3.0
+ - @backstage/plugin-search-backend-module-catalog@0.2.0
+ - @backstage/plugin-search-backend@1.5.15
+ - @backstage/catalog-model@1.6.0
+ - @backstage/plugin-auth-backend-module-guest-provider@0.1.9
+ - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.21
+ - @backstage/plugin-catalog-backend-module-unprocessed@0.4.10
+ - @backstage/plugin-permission-backend-module-allow-all-policy@0.1.20
+
+## example-backend-legacy@0.2.101
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-defaults@0.4.2
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-catalog-backend@1.25.0
+ - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.24
+ - @backstage/plugin-scaffolder-backend-module-rails@0.4.40
+ - @backstage/plugin-search-backend-node@1.3.0
+ - @backstage/plugin-scaffolder-backend@1.24.0
+ - @backstage/plugin-techdocs-backend@1.10.10
+ - @backstage/plugin-permission-common@0.8.1
+ - @backstage/plugin-search-backend-module-techdocs@0.2.0
+ - @backstage/plugin-search-backend-module-explore@0.2.0
+ - @backstage/plugin-kubernetes-backend@0.18.4
+ - @backstage/plugin-permission-backend@0.5.47
+ - @backstage/plugin-devtools-backend@0.3.9
+ - @backstage/plugin-signals-backend@0.1.9
+ - @backstage/plugin-proxy-backend@0.5.4
+ - @backstage/plugin-auth-backend@0.22.10
+ - @backstage/plugin-app-backend@0.3.72
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/plugin-permission-node@0.8.1
+ - @backstage/plugin-scaffolder-backend-module-gitlab@0.4.5
+ - @backstage/plugin-search-backend-module-catalog@0.2.0
+ - @backstage/plugin-search-backend@1.5.15
+ - @backstage/plugin-catalog-node@1.12.5
+ - @backstage/integration@1.14.0
+ - @backstage/plugin-search-backend-module-pg@0.5.33
+ - @backstage/catalog-model@1.6.0
+ - @backstage/catalog-client@1.6.6
+ - @backstage/config@1.2.0
+ - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.21
+ - @backstage/plugin-catalog-backend-module-unprocessed@0.4.10
+ - @backstage/plugin-events-backend@0.3.10
+ - @backstage/plugin-events-node@0.3.9
+ - @backstage/plugin-search-backend-module-elasticsearch@1.5.4
+ - @backstage/plugin-signals-node@0.1.9
+
+## e2e-test@0.2.19
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/create-app@0.5.18
+ - @backstage/cli-common@0.1.14
+ - @backstage/errors@1.2.4
+
+## techdocs-cli-embedded-app@0.2.99
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-catalog@1.22.0
+ - @backstage/cli@0.27.0
+ - @backstage/plugin-techdocs@1.10.8
+ - @backstage/core-components@0.14.10
+ - @backstage/core-app-api@1.14.2
+ - @backstage/catalog-model@1.6.0
+ - @backstage/app-defaults@1.5.10
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/integration-react@1.1.30
+ - @backstage/test-utils@1.5.10
+ - @backstage/theme@0.5.6
+ - @backstage/plugin-techdocs-react@1.2.7
+
+## @internal/plugin-todo-list@1.0.30
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/core-components@0.14.10
+ - @backstage/core-plugin-api@1.9.3
+
+## @internal/plugin-todo-list-backend@1.0.30
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.8.0
+ - @backstage/backend-common@0.24.0
+ - @backstage/plugin-auth-node@0.5.0
+ - @backstage/errors@1.2.4
+
+## @internal/plugin-todo-list-common@1.0.21
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-permission-common@0.8.1
diff --git a/docs/releases/v1.30.0-next.0-changelog.md b/docs/releases/v1.30.0-next.0-changelog.md
new file mode 100644
index 0000000000..f3b7f2ef45
--- /dev/null
+++ b/docs/releases/v1.30.0-next.0-changelog.md
@@ -0,0 +1,2214 @@
+# Release v1.30.0-next.0
+
+Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.30.0-next.0](https://backstage.github.io/upgrade-helper/?to=1.30.0-next.0)
+
+## @backstage/cli@0.27.0-next.0
+
+### Minor Changes
+
+- 32a38e1: **BREAKING**: The lockfile (`yarn.lock`) dependency analysis and mutations have been removed from several commands.
+
+ The `versions:bump` command will no longer attempt to bump and deduplicate dependencies by modifying the lockfile, it will only update `package.json` files.
+
+ The `versions:check` command has been removed, since its only purpose was verification and mutation of the lockfile. We recommend using the `yarn dedupe` command instead, or the `yarn-deduplicate` package if you're using Yarn classic.
+
+ The check that was built into the `package start` command has been removed, it will no longer warn about lockfile mismatches.
+
+ The packages in the Backstage ecosystem handle package duplications much better now than when these CLI features were first introduced, so the need for these features has diminished. By removing them, we drastically reduce the integration between the Backstage CLI and Yarn, making it much easier to add support for other package managers in the future.
+
+### Patch Changes
+
+- 7eb08a6: Add frontend-dynamic-container role to eslint config factory
+- fbc7819: Use ES2022 in CLI bundler
+- e53074f: Updated default backend plugin to use `RootConfigService` instead of `Config`. This also removes the dependency on `@backstage/config` as it's no longer used.
+- ee2b0e5: The experimental module federation build now has the ability to force the use of development versions of `react` and `react-dom` by setting the `FORCE_REACT_DEVELOPMENT` flag.
+- 239dffc: Remove usage of deprecated functionality from @backstage/config-loader
+- 0eedec3: Add support for dynamic plugins via the EXPERIMENTAL_MODULE_FEDERATION environment variable when running `yarn start`.
+- dc4fb4f: Fix for `repo build --all` not properly detecting the experimental public entry point.
+- Updated dependencies
+ - @backstage/integration@1.14.0-next.0
+ - @backstage/config-loader@1.8.2-next.0
+ - @backstage/catalog-model@1.5.0
+ - @backstage/cli-common@0.1.14
+ - @backstage/cli-node@0.2.7
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/eslint-plugin@0.1.8
+ - @backstage/release-manifests@0.0.11
+ - @backstage/types@1.1.1
+
+## @backstage/integration@1.14.0-next.0
+
+### Minor Changes
+
+- 78c1329: Updated `GitlabUrlReader.readUrl` and `GitlabUrlReader.readTree` to accept a user-provided token, supporting both bearer and private tokens.
+
+### Patch Changes
+
+- c591670: Updated functions for `getHarnessEditContentsUrl`, `getHarnessFileContentsUrl`, `getHarnessArchiveUrl`, `getHarnessLatestCommitUrl` and `parseHarnessUrl` to handle account and org level urls
+- Updated dependencies
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+
+## @backstage/plugin-scaffolder-react@1.11.0-next.0
+
+### Minor Changes
+
+- 8839381: Add scaffolder option to display object items in separate rows on review page
+
+### Patch Changes
+
+- d0e95a7: Add ability to customise form fields in the UI by exposing `uiSchema` and `formContext` in `FormProps`
+- 4670f06: support `ajv-errors` for scaffolder validation to allow for customizing the error messages
+- Updated dependencies
+ - @backstage/plugin-catalog-react@1.12.3-next.0
+ - @backstage/core-components@0.14.10-next.0
+ - @backstage/catalog-client@1.6.5
+ - @backstage/catalog-model@1.5.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/theme@0.5.6
+ - @backstage/types@1.1.1
+ - @backstage/version-bridge@1.0.8
+ - @backstage/plugin-permission-react@0.4.24
+ - @backstage/plugin-scaffolder-common@1.5.4
+
+## @backstage/app-defaults@1.5.9-next.0
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/core-components@0.14.10-next.0
+ - @backstage/core-app-api@1.14.1-next.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/theme@0.5.6
+ - @backstage/plugin-permission-react@0.4.24
+
+## @backstage/backend-app-api@0.8.1-next.0
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-common@0.23.4-next.0
+ - @backstage/config-loader@1.8.2-next.0
+ - @backstage/backend-plugin-api@0.7.1-next.0
+ - @backstage/backend-tasks@0.5.28-next.0
+ - @backstage/cli-common@0.1.14
+ - @backstage/cli-node@0.2.7
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-auth-node@0.4.18-next.0
+ - @backstage/plugin-permission-node@0.8.1-next.0
+
+## @backstage/backend-common@0.23.4-next.0
+
+### Patch Changes
+
+- ba8571e: Setup user agent header for AWS sdk clients, this enables users to better track API calls made from Backstage to AWS APIs through things like CloudTrail.
+
+- 7e13b7a: The remaining exports in the package have now been deprecated:
+
+ - `cacheToPluginCacheManager`
+ - `createLegacyAuthAdapters`
+ - `LegacyCreateRouter`
+ - `legacyPlugin`
+ - `loggerToWinstonLogger`
+ - `makeLegacyPlugin`
+
+ Users of these export should fully [migrate to the new backend system](https://backstage.io/docs/backend-system/building-backends/migrating).
+
+- Updated dependencies
+ - @backstage/integration@1.14.0-next.0
+ - @backstage/config-loader@1.8.2-next.0
+ - @backstage/backend-dev-utils@0.1.4
+ - @backstage/backend-plugin-api@0.7.1-next.0
+ - @backstage/cli-common@0.1.14
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/integration-aws-node@0.1.12
+ - @backstage/types@1.1.1
+ - @backstage/plugin-auth-node@0.4.18-next.0
+
+## @backstage/backend-defaults@0.4.2-next.0
+
+### Patch Changes
+
+- 4e79d19: The `createHealthRouter` utility that allows you to create a health check router is now exported via `@backstage/backend-defaults/rootHttpRouter`.
+- 78c1329: Updated `GitlabUrlReader.readUrl` and `GitlabUrlReader.readTree` to accept a user-provided token, supporting both bearer and private tokens.
+- 8e967da: Fixed the routing of the new health check service, the health endpoints should now properly be available at `/.backstage/health/v1/readiness` and `/.backstage/health/v1/liveness`.
+- Updated dependencies
+ - @backstage/backend-common@0.23.4-next.0
+ - @backstage/integration@1.14.0-next.0
+ - @backstage/config-loader@1.8.2-next.0
+ - @backstage/backend-app-api@0.8.1-next.0
+ - @backstage/backend-dev-utils@0.1.4
+ - @backstage/backend-plugin-api@0.7.1-next.0
+ - @backstage/cli-common@0.1.14
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/integration-aws-node@0.1.12
+ - @backstage/types@1.1.1
+ - @backstage/plugin-auth-node@0.4.18-next.0
+ - @backstage/plugin-events-node@0.3.9-next.0
+ - @backstage/plugin-permission-node@0.8.1-next.0
+
+## @backstage/backend-dynamic-feature-service@0.2.16-next.0
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-common@0.23.4-next.0
+ - @backstage/plugin-catalog-backend@1.24.1-next.0
+ - @backstage/config-loader@1.8.2-next.0
+ - @backstage/backend-app-api@0.8.1-next.0
+ - @backstage/backend-plugin-api@0.7.1-next.0
+ - @backstage/backend-tasks@0.5.28-next.0
+ - @backstage/cli-common@0.1.14
+ - @backstage/cli-node@0.2.7
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-app-node@0.1.23-next.0
+ - @backstage/plugin-auth-node@0.4.18-next.0
+ - @backstage/plugin-events-backend@0.3.10-next.0
+ - @backstage/plugin-events-node@0.3.9-next.0
+ - @backstage/plugin-permission-common@0.8.0
+ - @backstage/plugin-permission-node@0.8.1-next.0
+ - @backstage/plugin-scaffolder-node@0.4.9-next.0
+ - @backstage/plugin-search-backend-node@1.2.28-next.0
+ - @backstage/plugin-search-common@1.2.13
+
+## @backstage/backend-openapi-utils@0.1.16-next.0
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-plugin-api@0.7.1-next.0
+ - @backstage/errors@1.2.4
+
+## @backstage/backend-plugin-api@0.7.1-next.0
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/cli-common@0.1.14
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-auth-node@0.4.18-next.0
+ - @backstage/plugin-permission-common@0.8.0
+
+## @backstage/backend-tasks@0.5.28-next.0
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-common@0.23.4-next.0
+ - @backstage/backend-plugin-api@0.7.1-next.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/backend-test-utils@0.4.5-next.0
+
+### Patch Changes
+
+- 4e79d19: The default services for `startTestBackend` and `ServiceFactoryTester` now includes the Root Health Service.
+- Updated dependencies
+ - @backstage/backend-defaults@0.4.2-next.0
+ - @backstage/backend-app-api@0.8.1-next.0
+ - @backstage/backend-plugin-api@0.7.1-next.0
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+ - @backstage/plugin-auth-node@0.4.18-next.0
+ - @backstage/plugin-events-node@0.3.9-next.0
+
+## @backstage/config-loader@1.8.2-next.0
+
+### Patch Changes
+
+- 493feac: Add boolean `allowMissingDefaultConfig` option to `ConfigSources.default` and
+ `ConfigSources.defaultForTargets`, which results in omission of a ConfigSource
+ for the default app-config.yaml configuration file if it's not present.
+- Updated dependencies
+ - @backstage/cli-common@0.1.14
+ - @backstage/config@1.2.0
+ - @backstage/errors@1.2.4
+ - @backstage/types@1.1.1
+
+## @backstage/core-app-api@1.14.1-next.0
+
+### Patch Changes
+
+- 9a46a81: The request to delete the session cookie when running the app in protected mode is now done with a plain `fetch` rather than `FetchApi`. This fixes a bug where the app would immediately try to sign-in again when removing the cookie during logout.
+- Updated dependencies
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/types@1.1.1
+ - @backstage/version-bridge@1.0.8
+
+## @backstage/core-compat-api@0.2.8-next.0
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.6.8-next.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/version-bridge@1.0.8
+
+## @backstage/core-components@0.14.10-next.0
+
+### Patch Changes
+
+- 678971a: Move the `Link` component to the `RoutedTabs` instead of the `HeaderTabs` component
+- Updated dependencies
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/theme@0.5.6
+ - @backstage/version-bridge@1.0.8
+
+## @backstage/create-app@0.5.18-next.0
+
+### Patch Changes
+
+- c0a705d: Added the Kubernetes plugin to `create-app`
+- Updated dependencies
+ - @backstage/cli-common@0.1.14
+
+## @backstage/dev-utils@1.0.36-next.0
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-catalog-react@1.12.3-next.0
+ - @backstage/core-components@0.14.10-next.0
+ - @backstage/core-app-api@1.14.1-next.0
+ - @backstage/app-defaults@1.5.9-next.0
+ - @backstage/catalog-model@1.5.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/integration-react@1.1.30-next.0
+ - @backstage/theme@0.5.6
+
+## @backstage/frontend-app-api@0.7.4-next.0
+
+### Patch Changes
+
+- 7777b5f: Support icon overriding with the new `IconBundleBlueprint` API.
+- Updated dependencies
+ - @backstage/frontend-plugin-api@0.6.8-next.0
+ - @backstage/core-components@0.14.10-next.0
+ - @backstage/core-app-api@1.14.1-next.0
+ - @backstage/config@1.2.0
+ - @backstage/core-plugin-api@1.9.3
+ - @backstage/errors@1.2.4
+ - @backstage/theme@0.5.6
+ - @backstage/types@1.1.1
+ - @backstage/version-bridge@1.0.8
+
+## @backstage/frontend-plugin-api@0.6.8-next.0
+
+### Patch Changes
+
+- 4e53ad6: Introduce a new way to encapsulate extension kinds that replaces the extension creator pattern with `createExtensionBlueprint`
+
+ This allows the creation of extension instances with the following pattern:
+
+ ```tsx
+ // create the extension blueprint which is used to create instances
+ const EntityCardBlueprint = createExtensionBlueprint({
+ kind: 'entity-card',
+ attachTo: { id: 'test', input: 'default' },
+ output: {
+ element: coreExtensionData.reactElement,
+ },
+ factory(params: { text: string }) {
+ return {
+ element: