Merge branch 'backstage:master' into azure-devops-monorepo-support

This commit is contained in:
R-Beck-2020
2024-01-19 15:40:22 +00:00
committed by GitHub
994 changed files with 26417 additions and 11066 deletions
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-auth-backend': patch
---
Fixed an issue where some Okta's resolvers were missing
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/core-compat-api': patch
---
Make `convertLegacyApp` wrap discovered routes with `compatWrapper`.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-scaffolder-react': patch
---
Scaffolder form now shows a list of errors at the top of the form.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-sonarqube-backend': patch
---
Updated README
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/core-components': patch
---
Fixed bug in Link where it was possible to select and copy a hidden element into clipboard
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-compat-api': patch
---
The backwards compatibility provider will now use the new `ComponentsApi` and `IconsApi` when implementing the old `AppContext`.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/create-app': patch
---
Bumped create-app version.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-azure-sites': patch
---
Show Azure site tags in `EntityAzureSitesOverviewWidget`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-adr': patch
---
Updated README
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-azure-devops-backend': patch
---
Added a note about Service Principles
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-app-api': patch
---
Added `IconsApi` implementation and the ability to configure icons through the `icons` option for `createApp` and `createSpecializedApp`. This is not a long-term solution as icons should be installable via extensions instead. This is just a stop-gap to make sure there is feature parity with the existing frontend system.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-scaffolder-react': minor
'@backstage/plugin-scaffolder': minor
---
Remove the old legacy exports from `/alpha`
-7
View File
@@ -1,7 +0,0 @@
---
'@backstage/cli': patch
---
Use sha256 instead of md5 in build script cache key calculation
Makes it possible to build on FIPS nodejs.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/backend-test-utils': minor
'@backstage/backend-common': minor
---
drop databases after unit tests if the database instance is not running in docker
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': patch
---
Added initial `IconsApi` definition.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-kubernetes-backend': patch
---
Fixed an issue where a misleading error message would be logged when an
unsupported service locator method was specified.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/core-plugin-api': patch
---
Removed unnecessary `i18next` dependency.
-8
View File
@@ -1,8 +0,0 @@
---
'@backstage/backend-common': patch
---
Use sha256 instead of md5 for hash key calculation in caches
This can have a side effect of invalidating caches (when cache key was >250 characters)
This improves compliance with FIPS nodejs
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-kubernetes-react': patch
---
Fix broken XtermJS CSS import
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-scaffolder-backend': patch
---
Fixed an issue where not passing a `value` to any of the action's permission conditions caused an error.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-app-api': patch
---
Include the extension point ID and the module ID in the backend init error message.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': patch
---
Added basic support for the new frontend system, exported from the `/alpha` subpath.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-user-settings': minor
---
Updated the user settings selector to use a select component that displays native language names instead of language codes if possible.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-octopus-deploy': patch
---
added install path and fixed import on plugin-octopus-deploy
+11
View File
@@ -0,0 +1,11 @@
---
'@backstage/plugin-user-settings': patch
'@backstage/plugin-scaffolder': patch
'@backstage/plugin-tech-radar': patch
'@backstage/plugin-graphiql': patch
'@backstage/plugin-techdocs': patch
'@backstage/plugin-catalog': patch
'@backstage/plugin-search': patch
---
Use `convertLegacyRouteRefs` to define routes in `/alpha` export plugin.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-kubernetes-backend': patch
'@backstage/plugin-kubernetes-node': patch
---
Enabled a way to include custom auth metadata info on the clusters endpoint. If you want to implement a Kubernetes auth strategy which requires surfacing custom auth metadata to the frontend, use the new presentAuthMetadata method on the AuthenticationStrategy interface.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/catalog-client': patch
---
Fix a bug in `getLocationByRef` that led to invalid backend calls
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/repo-tools': patch
---
Updated the OpenAPI template to export the `TypedResponse` interface so that client code can leverage it
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog': patch
---
Fixed inconsistencies in icons used for System and Template
-9
View File
@@ -1,9 +0,0 @@
---
'@backstage/plugin-scaffolder-backend-module-confluence-to-markdown': patch
'@backstage/config-loader': patch
'@backstage/create-app': patch
'@backstage/repo-tools': patch
'@backstage/cli-node': patch
---
Removed `mock-fs` dev dependency.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-kubernetes-backend': patch
---
The purpose of this patch is to add a new login method which is `googleServiceAccount` configuring the kubernetes properties in the app-config.yaml file with authProvider key
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-entity-validation': patch
---
Improves UX of the EntityValidationPage: Moves the validate button below the EntityTextArea which is actually validated, the location TextField can now be hidden to prevent confusion about what is validated and additional content can be added to the top of the validation page.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-gitlab': patch
---
Added the option to provide custom `groupTransformer`, `userTransformer` and `groupNameTransformer` to allow custom transformations of groups and users
-8
View File
@@ -1,8 +0,0 @@
---
'@backstage/plugin-kubernetes-react': patch
'@backstage/plugin-kubernetes': patch
---
Add `authuser` search parameter to GKE cluster link formatter in k8s plugin
Thanks to this, people with multiple simultaneously logged-in accounts in their GCP console will automatically view objects with the same email as the one signed in to the Google auth provider in Backstage.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-compat-api': patch
---
Added `convertLegacyRouteRefs` for bulk conversion of plugin routes.
-8
View File
@@ -1,8 +0,0 @@
---
'@backstage/plugin-search-backend-module-stack-overflow-collator': patch
'@backstage/plugin-search-backend-module-techdocs': patch
'@backstage/plugin-search-backend-module-catalog': patch
'@backstage/plugin-search-backend-module-explore': patch
---
Update wording to show that the backend system no longer is in alpha
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-stack-overflow': patch
---
Fixes a bug that made the API return questions not related to the tags provided
+239 -260
View File
@@ -2,288 +2,267 @@
"mode": "pre",
"tag": "next",
"initialVersions": {
"example-app": "0.2.90",
"@backstage/app-defaults": "1.4.6",
"example-app-next": "0.0.4",
"app-next-example-plugin": "0.0.4",
"example-backend": "0.2.90",
"@backstage/backend-app-api": "0.5.9",
"@backstage/backend-common": "0.20.0",
"@backstage/backend-defaults": "0.2.8",
"@backstage/backend-dev-utils": "0.1.2",
"example-backend-next": "0.0.18",
"@backstage/backend-openapi-utils": "0.1.1",
"@backstage/backend-plugin-api": "0.6.8",
"@backstage/backend-plugin-manager": "0.0.4",
"@backstage/backend-tasks": "0.5.13",
"@backstage/backend-test-utils": "0.2.9",
"@backstage/catalog-client": "1.5.0",
"example-app": "0.2.91",
"@backstage/app-defaults": "1.4.7",
"example-app-next": "0.0.5",
"app-next-example-plugin": "0.0.5",
"example-backend": "0.2.91",
"@backstage/backend-app-api": "0.5.10",
"@backstage/backend-common": "0.20.1",
"@backstage/backend-defaults": "0.2.9",
"@backstage/backend-dev-utils": "0.1.3",
"@backstage/backend-dynamic-feature-service": "0.1.0",
"example-backend-next": "0.0.19",
"@backstage/backend-openapi-utils": "0.1.2",
"@backstage/backend-plugin-api": "0.6.9",
"@backstage/backend-tasks": "0.5.14",
"@backstage/backend-test-utils": "0.2.10",
"@backstage/catalog-client": "1.5.2",
"@backstage/catalog-model": "1.4.3",
"@backstage/cli": "0.25.0",
"@backstage/cli": "0.25.1",
"@backstage/cli-common": "0.1.13",
"@backstage/cli-node": "0.2.1",
"@backstage/cli-node": "0.2.2",
"@backstage/codemods": "0.1.46",
"@backstage/config": "1.1.1",
"@backstage/config-loader": "1.6.0",
"@backstage/core-app-api": "1.11.2",
"@backstage/core-compat-api": "0.1.0",
"@backstage/core-components": "0.13.9",
"@backstage/core-plugin-api": "1.8.1",
"@backstage/create-app": "0.5.8",
"@backstage/dev-utils": "1.0.25",
"e2e-test": "0.2.10",
"@backstage/config-loader": "1.6.1",
"@backstage/core-app-api": "1.11.3",
"@backstage/core-compat-api": "0.1.1",
"@backstage/core-components": "0.13.10",
"@backstage/core-plugin-api": "1.8.2",
"@backstage/create-app": "0.5.9",
"@backstage/dev-utils": "1.0.26",
"e2e-test": "0.2.11",
"@backstage/e2e-test-utils": "0.1.0",
"@backstage/errors": "1.2.3",
"@backstage/eslint-plugin": "0.1.4",
"@backstage/frontend-app-api": "0.4.0",
"@backstage/frontend-plugin-api": "0.4.0",
"@backstage/frontend-test-utils": "0.1.0",
"@backstage/frontend-app-api": "0.5.0",
"@backstage/frontend-plugin-api": "0.5.0",
"@backstage/frontend-test-utils": "0.1.1",
"@backstage/integration": "1.8.0",
"@backstage/integration-aws-node": "0.1.8",
"@backstage/integration-react": "1.1.22",
"@backstage/integration-react": "1.1.23",
"@backstage/release-manifests": "0.0.11",
"@backstage/repo-tools": "0.5.0",
"@techdocs/cli": "1.8.0",
"techdocs-cli-embedded-app": "0.2.89",
"@backstage/test-utils": "1.4.6",
"@backstage/repo-tools": "0.5.2",
"@techdocs/cli": "1.8.1",
"techdocs-cli-embedded-app": "0.2.90",
"@backstage/test-utils": "1.4.7",
"@backstage/theme": "0.5.0",
"@backstage/types": "1.1.1",
"@backstage/version-bridge": "1.0.7",
"@backstage/plugin-adr": "0.6.11",
"@backstage/plugin-adr-backend": "0.4.5",
"@backstage/plugin-adr-common": "0.2.18",
"@backstage/plugin-airbrake": "0.3.28",
"@backstage/plugin-airbrake-backend": "0.3.5",
"@backstage/plugin-allure": "0.1.44",
"@backstage/plugin-analytics-module-ga": "0.1.36",
"@backstage/plugin-analytics-module-ga4": "0.1.7",
"@backstage/plugin-analytics-module-newrelic-browser": "0.0.5",
"@backstage/plugin-apache-airflow": "0.2.18",
"@backstage/plugin-api-docs": "0.10.2",
"@backstage/plugin-adr": "0.6.12",
"@backstage/plugin-adr-backend": "0.4.6",
"@backstage/plugin-adr-common": "0.2.19",
"@backstage/plugin-airbrake": "0.3.29",
"@backstage/plugin-airbrake-backend": "0.3.6",
"@backstage/plugin-allure": "0.1.45",
"@backstage/plugin-analytics-module-ga": "0.1.37",
"@backstage/plugin-analytics-module-ga4": "0.1.8",
"@backstage/plugin-analytics-module-newrelic-browser": "0.0.6",
"@backstage/plugin-apache-airflow": "0.2.19",
"@backstage/plugin-api-docs": "0.10.3",
"@backstage/plugin-api-docs-module-protoc-gen-doc": "0.1.5",
"@backstage/plugin-apollo-explorer": "0.1.18",
"@backstage/plugin-app-backend": "0.3.56",
"@backstage/plugin-app-node": "0.1.8",
"@backstage/plugin-auth-backend": "0.20.1",
"@backstage/plugin-auth-backend-module-atlassian-provider": "0.1.0",
"@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.2.2",
"@backstage/plugin-auth-backend-module-github-provider": "0.1.5",
"@backstage/plugin-auth-backend-module-gitlab-provider": "0.1.5",
"@backstage/plugin-auth-backend-module-google-provider": "0.1.5",
"@backstage/plugin-auth-backend-module-microsoft-provider": "0.1.3",
"@backstage/plugin-auth-backend-module-oauth2-provider": "0.1.5",
"@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.1.0",
"@backstage/plugin-auth-backend-module-okta-provider": "0.0.1",
"@backstage/plugin-auth-backend-module-pinniped-provider": "0.1.2",
"@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.1.0",
"@backstage/plugin-auth-node": "0.4.2",
"@backstage/plugin-azure-devops": "0.3.10",
"@backstage/plugin-azure-devops-backend": "0.5.0",
"@backstage/plugin-apollo-explorer": "0.1.19",
"@backstage/plugin-app-backend": "0.3.57",
"@backstage/plugin-app-node": "0.1.9",
"@backstage/plugin-app-visualizer": "0.1.0",
"@backstage/plugin-auth-backend": "0.20.3",
"@backstage/plugin-auth-backend-module-atlassian-provider": "0.1.1",
"@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.2.3",
"@backstage/plugin-auth-backend-module-github-provider": "0.1.6",
"@backstage/plugin-auth-backend-module-gitlab-provider": "0.1.6",
"@backstage/plugin-auth-backend-module-google-provider": "0.1.6",
"@backstage/plugin-auth-backend-module-microsoft-provider": "0.1.4",
"@backstage/plugin-auth-backend-module-oauth2-provider": "0.1.6",
"@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.1.1",
"@backstage/plugin-auth-backend-module-okta-provider": "0.0.2",
"@backstage/plugin-auth-backend-module-pinniped-provider": "0.1.3",
"@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.1.1",
"@backstage/plugin-auth-node": "0.4.3",
"@backstage/plugin-azure-devops": "0.3.11",
"@backstage/plugin-azure-devops-backend": "0.5.1",
"@backstage/plugin-azure-devops-common": "0.3.2",
"@backstage/plugin-azure-sites": "0.1.17",
"@backstage/plugin-azure-sites-backend": "0.1.18",
"@backstage/plugin-azure-sites": "0.1.18",
"@backstage/plugin-azure-sites-backend": "0.1.19",
"@backstage/plugin-azure-sites-common": "0.1.1",
"@backstage/plugin-badges": "0.2.52",
"@backstage/plugin-badges-backend": "0.3.5",
"@backstage/plugin-bazaar": "0.2.20",
"@backstage/plugin-bazaar-backend": "0.3.6",
"@backstage/plugin-badges": "0.2.53",
"@backstage/plugin-badges-backend": "0.3.6",
"@backstage/plugin-bazaar": "0.2.21",
"@backstage/plugin-bazaar-backend": "0.3.7",
"@backstage/plugin-bitbucket-cloud-common": "0.2.15",
"@backstage/plugin-bitrise": "0.1.55",
"@backstage/plugin-catalog": "1.16.0",
"@backstage/plugin-catalog-backend": "1.16.0",
"@backstage/plugin-catalog-backend-module-aws": "0.3.2",
"@backstage/plugin-catalog-backend-module-azure": "0.1.27",
"@backstage/plugin-catalog-backend-module-backstage-openapi": "0.1.1",
"@backstage/plugin-catalog-backend-module-bitbucket": "0.2.23",
"@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.1.23",
"@backstage/plugin-catalog-backend-module-bitbucket-server": "0.1.21",
"@backstage/plugin-catalog-backend-module-gcp": "0.1.8",
"@backstage/plugin-catalog-backend-module-gerrit": "0.1.24",
"@backstage/plugin-catalog-backend-module-github": "0.4.6",
"@backstage/plugin-catalog-backend-module-github-org": "0.1.2",
"@backstage/plugin-catalog-backend-module-gitlab": "0.3.5",
"@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.4.12",
"@backstage/plugin-catalog-backend-module-ldap": "0.5.23",
"@backstage/plugin-catalog-backend-module-msgraph": "0.5.15",
"@backstage/plugin-catalog-backend-module-openapi": "0.1.25",
"@backstage/plugin-catalog-backend-module-puppetdb": "0.1.13",
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.1.5",
"@backstage/plugin-catalog-backend-module-unprocessed": "0.3.5",
"@backstage/plugin-catalog-common": "1.0.19",
"@backstage/plugin-catalog-graph": "0.3.2",
"@backstage/plugin-catalog-import": "0.10.4",
"@backstage/plugin-catalog-node": "1.6.0",
"@backstage/plugin-catalog-react": "1.9.2",
"@backstage/plugin-catalog-unprocessed-entities": "0.1.6",
"@backstage/plugin-cicd-statistics": "0.1.30",
"@backstage/plugin-cicd-statistics-module-gitlab": "0.1.24",
"@backstage/plugin-circleci": "0.3.28",
"@backstage/plugin-cloudbuild": "0.3.28",
"@backstage/plugin-code-climate": "0.1.28",
"@backstage/plugin-code-coverage": "0.2.21",
"@backstage/plugin-code-coverage-backend": "0.2.22",
"@backstage/plugin-codescene": "0.1.20",
"@backstage/plugin-config-schema": "0.1.48",
"@backstage/plugin-cost-insights": "0.12.17",
"@backstage/plugin-bitrise": "0.1.56",
"@backstage/plugin-catalog": "1.16.1",
"@backstage/plugin-catalog-backend": "1.16.1",
"@backstage/plugin-catalog-backend-module-aws": "0.3.3",
"@backstage/plugin-catalog-backend-module-azure": "0.1.28",
"@backstage/plugin-catalog-backend-module-backstage-openapi": "0.1.2",
"@backstage/plugin-catalog-backend-module-bitbucket": "0.2.24",
"@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.1.24",
"@backstage/plugin-catalog-backend-module-bitbucket-server": "0.1.22",
"@backstage/plugin-catalog-backend-module-gcp": "0.1.9",
"@backstage/plugin-catalog-backend-module-gerrit": "0.1.25",
"@backstage/plugin-catalog-backend-module-github": "0.4.7",
"@backstage/plugin-catalog-backend-module-github-org": "0.1.3",
"@backstage/plugin-catalog-backend-module-gitlab": "0.3.6",
"@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.4.13",
"@backstage/plugin-catalog-backend-module-ldap": "0.5.24",
"@backstage/plugin-catalog-backend-module-msgraph": "0.5.16",
"@backstage/plugin-catalog-backend-module-openapi": "0.1.26",
"@backstage/plugin-catalog-backend-module-puppetdb": "0.1.14",
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.1.6",
"@backstage/plugin-catalog-backend-module-unprocessed": "0.3.6",
"@backstage/plugin-catalog-common": "1.0.20",
"@backstage/plugin-catalog-graph": "0.3.3",
"@backstage/plugin-catalog-import": "0.10.5",
"@backstage/plugin-catalog-node": "1.6.1",
"@backstage/plugin-catalog-react": "1.9.3",
"@backstage/plugin-catalog-unprocessed-entities": "0.1.7",
"@backstage/plugin-cicd-statistics": "0.1.31",
"@backstage/plugin-cicd-statistics-module-gitlab": "0.1.25",
"@backstage/plugin-circleci": "0.3.29",
"@backstage/plugin-cloudbuild": "0.3.29",
"@backstage/plugin-code-climate": "0.1.29",
"@backstage/plugin-code-coverage": "0.2.22",
"@backstage/plugin-code-coverage-backend": "0.2.23",
"@backstage/plugin-codescene": "0.1.21",
"@backstage/plugin-config-schema": "0.1.49",
"@backstage/plugin-cost-insights": "0.12.18",
"@backstage/plugin-cost-insights-common": "0.1.2",
"@backstage/plugin-devtools": "0.1.7",
"@backstage/plugin-devtools-backend": "0.2.5",
"@backstage/plugin-devtools-common": "0.1.7",
"@backstage/plugin-dynatrace": "8.0.2",
"@backstage/plugin-entity-feedback": "0.2.11",
"@backstage/plugin-entity-feedback-backend": "0.2.5",
"@backstage/plugin-devtools": "0.1.8",
"@backstage/plugin-devtools-backend": "0.2.6",
"@backstage/plugin-devtools-common": "0.1.8",
"@backstage/plugin-dynatrace": "8.0.3",
"@backstage/plugin-entity-feedback": "0.2.12",
"@backstage/plugin-entity-feedback-backend": "0.2.6",
"@backstage/plugin-entity-feedback-common": "0.1.3",
"@backstage/plugin-entity-validation": "0.1.13",
"@backstage/plugin-events-backend": "0.2.17",
"@backstage/plugin-events-backend-module-aws-sqs": "0.2.11",
"@backstage/plugin-events-backend-module-azure": "0.1.18",
"@backstage/plugin-events-backend-module-bitbucket-cloud": "0.1.18",
"@backstage/plugin-events-backend-module-gerrit": "0.1.18",
"@backstage/plugin-events-backend-module-github": "0.1.18",
"@backstage/plugin-events-backend-module-gitlab": "0.1.18",
"@backstage/plugin-events-backend-test-utils": "0.1.18",
"@backstage/plugin-events-node": "0.2.17",
"@internal/plugin-todo-list": "1.0.20",
"@internal/plugin-todo-list-backend": "1.0.20",
"@internal/plugin-todo-list-common": "1.0.16",
"@backstage/plugin-explore": "0.4.14",
"@backstage/plugin-explore-backend": "0.0.18",
"@backstage/plugin-entity-validation": "0.1.14",
"@backstage/plugin-events-backend": "0.2.18",
"@backstage/plugin-events-backend-module-aws-sqs": "0.2.12",
"@backstage/plugin-events-backend-module-azure": "0.1.19",
"@backstage/plugin-events-backend-module-bitbucket-cloud": "0.1.19",
"@backstage/plugin-events-backend-module-gerrit": "0.1.19",
"@backstage/plugin-events-backend-module-github": "0.1.19",
"@backstage/plugin-events-backend-module-gitlab": "0.1.19",
"@backstage/plugin-events-backend-test-utils": "0.1.19",
"@backstage/plugin-events-node": "0.2.18",
"@internal/plugin-todo-list": "1.0.21",
"@internal/plugin-todo-list-backend": "1.0.21",
"@internal/plugin-todo-list-common": "1.0.17",
"@backstage/plugin-explore": "0.4.15",
"@backstage/plugin-explore-backend": "0.0.19",
"@backstage/plugin-explore-common": "0.0.2",
"@backstage/plugin-explore-react": "0.0.34",
"@backstage/plugin-firehydrant": "0.2.12",
"@backstage/plugin-fossa": "0.2.60",
"@backstage/plugin-gcalendar": "0.3.21",
"@backstage/plugin-gcp-projects": "0.3.44",
"@backstage/plugin-git-release-manager": "0.3.40",
"@backstage/plugin-github-actions": "0.6.9",
"@backstage/plugin-github-deployments": "0.1.59",
"@backstage/plugin-github-issues": "0.2.17",
"@backstage/plugin-github-pull-requests-board": "0.1.22",
"@backstage/plugin-gitops-profiles": "0.3.43",
"@backstage/plugin-gocd": "0.1.34",
"@backstage/plugin-graphiql": "0.3.1",
"@backstage/plugin-graphql-voyager": "0.1.10",
"@backstage/plugin-home": "0.6.0",
"@backstage/plugin-home-react": "0.1.6",
"@backstage/plugin-ilert": "0.2.17",
"@backstage/plugin-jenkins": "0.9.3",
"@backstage/plugin-jenkins-backend": "0.3.2",
"@backstage/plugin-jenkins-common": "0.1.22",
"@backstage/plugin-kafka": "0.3.28",
"@backstage/plugin-kafka-backend": "0.3.6",
"@backstage/plugin-kubernetes": "0.11.3",
"@backstage/plugin-kubernetes-backend": "0.14.0",
"@backstage/plugin-kubernetes-cluster": "0.0.4",
"@backstage/plugin-kubernetes-common": "0.7.2",
"@backstage/plugin-kubernetes-node": "0.1.2",
"@backstage/plugin-kubernetes-react": "0.2.0",
"@backstage/plugin-lighthouse": "0.4.13",
"@backstage/plugin-lighthouse-backend": "0.4.0",
"@backstage/plugin-explore-react": "0.0.35",
"@backstage/plugin-firehydrant": "0.2.13",
"@backstage/plugin-fossa": "0.2.61",
"@backstage/plugin-gcalendar": "0.3.22",
"@backstage/plugin-gcp-projects": "0.3.45",
"@backstage/plugin-git-release-manager": "0.3.41",
"@backstage/plugin-github-actions": "0.6.10",
"@backstage/plugin-github-deployments": "0.1.60",
"@backstage/plugin-github-issues": "0.2.18",
"@backstage/plugin-github-pull-requests-board": "0.1.23",
"@backstage/plugin-gitops-profiles": "0.3.44",
"@backstage/plugin-gocd": "0.1.35",
"@backstage/plugin-graphiql": "0.3.2",
"@backstage/plugin-graphql-voyager": "0.1.11",
"@backstage/plugin-home": "0.6.1",
"@backstage/plugin-home-react": "0.1.7",
"@backstage/plugin-ilert": "0.2.18",
"@backstage/plugin-jenkins": "0.9.4",
"@backstage/plugin-jenkins-backend": "0.3.3",
"@backstage/plugin-jenkins-common": "0.1.23",
"@backstage/plugin-kafka": "0.3.29",
"@backstage/plugin-kafka-backend": "0.3.7",
"@backstage/plugin-kubernetes": "0.11.4",
"@backstage/plugin-kubernetes-backend": "0.14.1",
"@backstage/plugin-kubernetes-cluster": "0.0.5",
"@backstage/plugin-kubernetes-common": "0.7.3",
"@backstage/plugin-kubernetes-node": "0.1.3",
"@backstage/plugin-kubernetes-react": "0.2.1",
"@backstage/plugin-lighthouse": "0.4.14",
"@backstage/plugin-lighthouse-backend": "0.4.1",
"@backstage/plugin-lighthouse-common": "0.1.4",
"@backstage/plugin-linguist": "0.1.13",
"@backstage/plugin-linguist-backend": "0.5.5",
"@backstage/plugin-linguist": "0.1.14",
"@backstage/plugin-linguist-backend": "0.5.6",
"@backstage/plugin-linguist-common": "0.1.2",
"@backstage/plugin-microsoft-calendar": "0.1.10",
"@backstage/plugin-newrelic": "0.3.43",
"@backstage/plugin-newrelic-dashboard": "0.3.3",
"@backstage/plugin-nomad": "0.1.9",
"@backstage/plugin-nomad-backend": "0.1.10",
"@backstage/plugin-octopus-deploy": "0.2.10",
"@backstage/plugin-opencost": "0.2.3",
"@backstage/plugin-org": "0.6.18",
"@backstage/plugin-org-react": "0.1.17",
"@backstage/plugin-pagerduty": "0.7.0",
"@backstage/plugin-periskop": "0.1.26",
"@backstage/plugin-periskop-backend": "0.2.6",
"@backstage/plugin-permission-backend": "0.5.31",
"@backstage/plugin-permission-backend-module-allow-all-policy": "0.1.5",
"@backstage/plugin-permission-common": "0.7.11",
"@backstage/plugin-permission-node": "0.7.19",
"@backstage/plugin-permission-react": "0.4.18",
"@backstage/plugin-playlist": "0.2.2",
"@backstage/plugin-playlist-backend": "0.3.12",
"@backstage/plugin-playlist-common": "0.1.13",
"@backstage/plugin-proxy-backend": "0.4.6",
"@backstage/plugin-puppetdb": "0.1.11",
"@backstage/plugin-rollbar": "0.4.28",
"@backstage/plugin-rollbar-backend": "0.1.53",
"@backstage/plugin-scaffolder": "1.17.0",
"@backstage/plugin-scaffolder-backend": "1.19.2",
"@backstage/plugin-scaffolder-backend-module-azure": "0.1.0",
"@backstage/plugin-scaffolder-backend-module-bitbucket": "0.1.0",
"@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.2.9",
"@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.32",
"@backstage/plugin-scaffolder-backend-module-gerrit": "0.1.0",
"@backstage/plugin-scaffolder-backend-module-github": "0.1.0",
"@backstage/plugin-scaffolder-backend-module-gitlab": "0.2.11",
"@backstage/plugin-scaffolder-backend-module-rails": "0.4.25",
"@backstage/plugin-scaffolder-backend-module-sentry": "0.1.16",
"@backstage/plugin-scaffolder-backend-module-yeoman": "0.2.29",
"@backstage/plugin-scaffolder-common": "1.4.4",
"@backstage/plugin-scaffolder-node": "0.2.9",
"@backstage/plugin-scaffolder-react": "1.7.0",
"@backstage/plugin-search": "1.4.4",
"@backstage/plugin-search-backend": "1.4.8",
"@backstage/plugin-search-backend-module-catalog": "0.1.12",
"@backstage/plugin-search-backend-module-elasticsearch": "1.3.11",
"@backstage/plugin-search-backend-module-explore": "0.1.12",
"@backstage/plugin-search-backend-module-pg": "0.5.17",
"@backstage/plugin-search-backend-module-stack-overflow-collator": "0.1.1",
"@backstage/plugin-search-backend-module-techdocs": "0.1.12",
"@backstage/plugin-search-backend-node": "1.2.12",
"@backstage/plugin-search-common": "1.2.9",
"@backstage/plugin-search-react": "1.7.4",
"@backstage/plugin-sentry": "0.5.13",
"@backstage/plugin-shortcuts": "0.3.17",
"@backstage/plugin-sonarqube": "0.7.10",
"@backstage/plugin-sonarqube-backend": "0.2.10",
"@backstage/plugin-sonarqube-react": "0.1.11",
"@backstage/plugin-splunk-on-call": "0.4.17",
"@backstage/plugin-stack-overflow": "0.1.23",
"@backstage/plugin-stack-overflow-backend": "0.2.12",
"@backstage/plugin-stackstorm": "0.1.9",
"@backstage/plugin-tech-insights": "0.3.20",
"@backstage/plugin-tech-insights-backend": "0.5.22",
"@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.40",
"@backstage/plugin-microsoft-calendar": "0.1.11",
"@backstage/plugin-newrelic": "0.3.44",
"@backstage/plugin-newrelic-dashboard": "0.3.4",
"@backstage/plugin-nomad": "0.1.10",
"@backstage/plugin-nomad-backend": "0.1.11",
"@backstage/plugin-octopus-deploy": "0.2.11",
"@backstage/plugin-opencost": "0.2.4",
"@backstage/plugin-org": "0.6.19",
"@backstage/plugin-org-react": "0.1.18",
"@backstage/plugin-pagerduty": "0.7.1",
"@backstage/plugin-periskop": "0.1.27",
"@backstage/plugin-periskop-backend": "0.2.7",
"@backstage/plugin-permission-backend": "0.5.32",
"@backstage/plugin-permission-backend-module-allow-all-policy": "0.1.6",
"@backstage/plugin-permission-common": "0.7.12",
"@backstage/plugin-permission-node": "0.7.20",
"@backstage/plugin-permission-react": "0.4.19",
"@backstage/plugin-playlist": "0.2.3",
"@backstage/plugin-playlist-backend": "0.3.13",
"@backstage/plugin-playlist-common": "0.1.14",
"@backstage/plugin-proxy-backend": "0.4.7",
"@backstage/plugin-puppetdb": "0.1.12",
"@backstage/plugin-rollbar": "0.4.29",
"@backstage/plugin-rollbar-backend": "0.1.54",
"@backstage/plugin-scaffolder": "1.17.1",
"@backstage/plugin-scaffolder-backend": "1.20.0",
"@backstage/plugin-scaffolder-backend-module-azure": "0.1.1",
"@backstage/plugin-scaffolder-backend-module-bitbucket": "0.1.1",
"@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.2.10",
"@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.33",
"@backstage/plugin-scaffolder-backend-module-gerrit": "0.1.1",
"@backstage/plugin-scaffolder-backend-module-github": "0.1.1",
"@backstage/plugin-scaffolder-backend-module-gitlab": "0.2.12",
"@backstage/plugin-scaffolder-backend-module-rails": "0.4.26",
"@backstage/plugin-scaffolder-backend-module-sentry": "0.1.17",
"@backstage/plugin-scaffolder-backend-module-yeoman": "0.2.30",
"@backstage/plugin-scaffolder-common": "1.4.5",
"@backstage/plugin-scaffolder-node": "0.2.10",
"@backstage/plugin-scaffolder-react": "1.7.1",
"@backstage/plugin-search": "1.4.5",
"@backstage/plugin-search-backend": "1.4.9",
"@backstage/plugin-search-backend-module-catalog": "0.1.13",
"@backstage/plugin-search-backend-module-elasticsearch": "1.3.12",
"@backstage/plugin-search-backend-module-explore": "0.1.13",
"@backstage/plugin-search-backend-module-pg": "0.5.18",
"@backstage/plugin-search-backend-module-stack-overflow-collator": "0.1.2",
"@backstage/plugin-search-backend-module-techdocs": "0.1.13",
"@backstage/plugin-search-backend-node": "1.2.13",
"@backstage/plugin-search-common": "1.2.10",
"@backstage/plugin-search-react": "1.7.5",
"@backstage/plugin-sentry": "0.5.14",
"@backstage/plugin-shortcuts": "0.3.18",
"@backstage/plugin-sonarqube": "0.7.11",
"@backstage/plugin-sonarqube-backend": "0.2.11",
"@backstage/plugin-sonarqube-react": "0.1.12",
"@backstage/plugin-splunk-on-call": "0.4.18",
"@backstage/plugin-stack-overflow": "0.1.24",
"@backstage/plugin-stack-overflow-backend": "0.2.13",
"@backstage/plugin-stackstorm": "0.1.10",
"@backstage/plugin-tech-insights": "0.3.21",
"@backstage/plugin-tech-insights-backend": "0.5.23",
"@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.41",
"@backstage/plugin-tech-insights-common": "0.2.12",
"@backstage/plugin-tech-insights-node": "0.4.14",
"@backstage/plugin-tech-radar": "0.6.11",
"@backstage/plugin-techdocs": "1.9.2",
"@backstage/plugin-techdocs-addons-test-utils": "1.0.25",
"@backstage/plugin-techdocs-backend": "1.9.1",
"@backstage/plugin-techdocs-module-addons-contrib": "1.1.3",
"@backstage/plugin-techdocs-node": "1.11.0",
"@backstage/plugin-techdocs-react": "1.1.14",
"@backstage/plugin-todo": "0.2.32",
"@backstage/plugin-todo-backend": "0.3.6",
"@backstage/plugin-user-settings": "0.7.14",
"@backstage/plugin-user-settings-backend": "0.2.7",
"@backstage/plugin-vault": "0.1.23",
"@backstage/plugin-vault-backend": "0.4.1",
"@backstage/plugin-vault-node": "0.1.1",
"@backstage/plugin-visualizer": "0.0.1",
"@backstage/plugin-xcmetrics": "0.2.46"
"@backstage/plugin-tech-insights-node": "0.4.15",
"@backstage/plugin-tech-radar": "0.6.12",
"@backstage/plugin-techdocs": "1.9.3",
"@backstage/plugin-techdocs-addons-test-utils": "1.0.26",
"@backstage/plugin-techdocs-backend": "1.9.2",
"@backstage/plugin-techdocs-module-addons-contrib": "1.1.4",
"@backstage/plugin-techdocs-node": "1.11.1",
"@backstage/plugin-techdocs-react": "1.1.15",
"@backstage/plugin-todo": "0.2.33",
"@backstage/plugin-todo-backend": "0.3.7",
"@backstage/plugin-user-settings": "0.8.0",
"@backstage/plugin-user-settings-backend": "0.2.8",
"@backstage/plugin-vault": "0.1.24",
"@backstage/plugin-vault-backend": "0.4.2",
"@backstage/plugin-vault-node": "0.1.2",
"@backstage/plugin-xcmetrics": "0.2.47"
},
"changesets": [
"afraid-mice-give",
"brown-planes-sort",
"chilled-seahorses-clean",
"create-app-1703547661",
"curvy-dingos-jump",
"fast-tables-hammer",
"friendly-horses-kneel",
"funny-rings-fry",
"gold-pianos-worry",
"happy-forks-jam",
"large-poets-buy",
"lazy-teachers-walk",
"many-peaches-dress",
"new-plants-sort",
"orange-planets-suffer",
"renovate-126fde4",
"renovate-9647667",
"rotten-tools-clean",
"sour-taxis-rush",
"stale-hairs-sparkle"
]
"changesets": []
}
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/plugin-signals-backend': patch
'@backstage/plugin-signals': patch
'@backstage/plugin-signals-react': patch
'@backstage/plugin-signals-node': patch
---
Add support to subscribe and publish messages through signals plugins
-6
View File
@@ -1,6 +0,0 @@
---
'@backstage/plugin-github-issues': patch
'@backstage/plugin-scaffolder-backend-module-github': patch
---
Updated dependency `octokit` to `^3.0.0`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Updated dependency `vite-plugin-node-polyfills` to `^0.19.0`.
-11
View File
@@ -1,11 +0,0 @@
---
'@backstage/plugin-home-react': patch
'@backstage/plugin-home': patch
'@backstage/plugin-scaffolder-react': patch
'@backstage/plugin-scaffolder': patch
---
Updated dependency `@rjsf/utils` to `5.15.1`.
Updated dependency `@rjsf/core` to `5.15.1`.
Updated dependency `@rjsf/material-ui` to `5.15.1`.
Updated dependency `@rjsf/validator-ajv8` to `5.15.1`.
-6
View File
@@ -1,6 +0,0 @@
---
'@backstage/plugin-azure-devops-backend': patch
'@backstage/plugin-scaffolder-backend-module-azure': patch
---
Updated dependency `azure-devops-node-api` to `^12.0.0`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-api-docs': patch
---
Updated dependency `@asyncapi/react-component` to `1.2.13`.
-145
View File
@@ -1,145 +0,0 @@
---
'@backstage/plugin-scaffolder-backend-module-confluence-to-markdown': patch
'@backstage/plugin-catalog-backend-module-scaffolder-entity-model': patch
'@backstage/plugin-permission-backend-module-allow-all-policy': patch
'@backstage/plugin-auth-backend-module-oauth2-proxy-provider': patch
'@backstage/plugin-auth-backend-module-vmware-cloud-provider': patch
'@backstage/plugin-catalog-backend-module-backstage-openapi': patch
'@backstage/plugin-auth-backend-module-atlassian-provider': patch
'@backstage/plugin-auth-backend-module-microsoft-provider': patch
'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch
'@backstage/plugin-auth-backend-module-pinniped-provider': patch
'@backstage/plugin-events-backend-module-bitbucket-cloud': patch
'@backstage/plugin-auth-backend-module-gcp-iap-provider': patch
'@backstage/plugin-auth-backend-module-github-provider': patch
'@backstage/plugin-auth-backend-module-gitlab-provider': patch
'@backstage/plugin-auth-backend-module-google-provider': patch
'@backstage/plugin-auth-backend-module-oauth2-provider': patch
'@backstage/plugin-tech-insights-backend-module-jsonfc': patch
'@backstage/plugin-analytics-module-newrelic-browser': patch
'@backstage/plugin-auth-backend-module-okta-provider': patch
'@backstage/plugin-catalog-backend-module-bitbucket': patch
'@backstage/plugin-scaffolder-backend-module-yeoman': patch
'@backstage/plugin-techdocs-module-addons-contrib': patch
'@backstage/plugin-catalog-backend-module-gerrit': patch
'@backstage/plugin-catalog-backend-module-azure': patch
'@backstage/plugin-catalog-unprocessed-entities': 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-module-azure': patch
'@backstage/plugin-catalog-backend-module-aws': patch
'@backstage/plugin-github-pull-requests-board': patch
'@backstage/plugin-techdocs-addons-test-utils': patch
'@backstage/plugin-entity-feedback-backend': patch
'@backstage/backend-openapi-utils': patch
'@backstage/plugin-stack-overflow-backend': patch
'@backstage/plugin-code-coverage-backend': patch
'@backstage/plugin-user-settings-backend': patch
'@backstage/frontend-plugin-api': patch
'@backstage/plugin-analytics-module-ga4': patch
'@backstage/plugin-azure-devops-backend': patch
'@backstage/plugin-analytics-module-ga': patch
'@backstage/plugin-azure-sites-backend': patch
'@backstage/plugin-git-release-manager': patch
'@backstage/plugin-github-deployments': patch
'@backstage/plugin-kubernetes-cluster': patch
'@backstage/plugin-microsoft-calendar': patch
'@backstage/plugin-newrelic-dashboard': patch
'@backstage/plugin-tech-insights-node': patch
'@backstage/plugin-entity-validation': patch
'@backstage/plugin-kubernetes-common': patch
'@backstage/plugin-airbrake-backend': patch
'@backstage/plugin-devtools-backend': patch
'@backstage/plugin-linguist-backend': patch
'@backstage/plugin-periskop-backend': patch
'@backstage/plugin-permission-react': patch
'@backstage/plugin-playlist-backend': patch
'@backstage/plugin-scaffolder-react': patch
'@backstage/plugin-techdocs-backend': patch
'@backstage/plugin-apollo-explorer': patch
'@backstage/plugin-entity-feedback': patch
'@backstage/plugin-explore-backend': patch
'@backstage/plugin-gitops-profiles': patch
'@backstage/plugin-graphql-voyager': patch
'@backstage/plugin-jenkins-backend': patch
'@backstage/plugin-apache-airflow': patch
'@backstage/plugin-badges-backend': patch
'@backstage/plugin-bazaar-backend': patch
'@backstage/plugin-github-actions': patch
'@backstage/plugin-octopus-deploy': patch
'@backstage/plugin-search-backend': patch
'@backstage/plugin-splunk-on-call': patch
'@backstage/plugin-stack-overflow': patch
'@backstage/plugin-techdocs-react': patch
'@backstage/plugin-catalog-graph': patch
'@backstage/plugin-catalog-react': patch
'@backstage/plugin-code-coverage': patch
'@backstage/plugin-config-schema': patch
'@backstage/plugin-cost-insights': patch
'@backstage/plugin-explore-react': patch
'@backstage/plugin-github-issues': patch
'@backstage/plugin-kafka-backend': patch
'@backstage/plugin-nomad-backend': patch
'@backstage/plugin-tech-insights': patch
'@backstage/plugin-user-settings': patch
'@backstage/plugin-auth-backend': patch
'@backstage/plugin-azure-devops': patch
'@backstage/plugin-catalog-node': patch
'@backstage/plugin-code-climate': patch
'@backstage/plugin-gcp-projects': patch
'@backstage/plugin-todo-backend': patch
'@backstage/plugin-adr-backend': patch
'@backstage/plugin-app-backend': patch
'@backstage/plugin-azure-sites': patch
'@backstage/plugin-firehydrant': patch
'@backstage/plugin-cloudbuild': patch
'@backstage/plugin-home-react': patch
'@backstage/plugin-kubernetes': patch
'@backstage/plugin-lighthouse': patch
'@backstage/plugin-scaffolder': patch
'@backstage/plugin-stackstorm': patch
'@backstage/plugin-tech-radar': patch
'@backstage/plugin-codescene': patch
'@backstage/plugin-dynatrace': patch
'@backstage/plugin-gcalendar': patch
'@backstage/plugin-org-react': patch
'@backstage/plugin-pagerduty': patch
'@backstage/plugin-shortcuts': patch
'@backstage/plugin-sonarqube': patch
'@backstage/plugin-xcmetrics': patch
'@backstage/plugin-airbrake': patch
'@backstage/plugin-api-docs': patch
'@backstage/plugin-circleci': patch
'@backstage/plugin-devtools': patch
'@backstage/plugin-graphiql': patch
'@backstage/plugin-linguist': patch
'@backstage/plugin-newrelic': patch
'@backstage/plugin-opencost': patch
'@backstage/plugin-periskop': patch
'@backstage/plugin-playlist': patch
'@backstage/plugin-puppetdb': patch
'@backstage/plugin-techdocs': patch
'@backstage/plugin-bitrise': patch
'@backstage/plugin-catalog': patch
'@backstage/plugin-explore': patch
'@backstage/plugin-jenkins': patch
'@backstage/plugin-rollbar': patch
'@backstage/plugin-allure': patch
'@backstage/plugin-badges': patch
'@backstage/plugin-bazaar': patch
'@backstage/plugin-search': patch
'@backstage/plugin-sentry': patch
'@backstage/plugin-fossa': patch
'@backstage/plugin-ilert': patch
'@backstage/plugin-kafka': patch
'@backstage/plugin-nomad': patch
'@backstage/plugin-vault': patch
'@backstage/plugin-gocd': patch
'@backstage/plugin-home': patch
'@backstage/plugin-todo': patch
'@backstage/plugin-adr': patch
'@backstage/plugin-org': patch
---
Remove some unused dependencies
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-bitrise': patch
---
Update README
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-kubernetes-react': patch
---
Fixed a bug where the logs dialog and any other functionality depending on the proxy endpoint would fail for clusters configured with the OIDC auth provider.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-kubernetes-react': minor
---
**BREAKING** The pod exec terminal is now disabled by default since there are several scenarios where it is known not to work. It can be re-enabled at your own risk by setting the config parameter `kubernetes.podExecTerminal.enabled` to `true`.
+9
View File
@@ -0,0 +1,9 @@
---
'@backstage/create-app': patch
---
Updated `packages/app` as well as the root `package.json` type resolutions to use React v18.
The `@testing-library/*` dependencies have also been updated to the ones compatible with React v18, and the test at `packages/app/src/App.test.tsx` had been updated to use more modern patterns that work better with these new versions.
For information on how to migrate existing apps to React v18, see the [migration guide](https://backstage.io/docs/tutorials/react18-migration)
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/core-components': patch
---
Removed unnecessary `history` and `immer` dependencies.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-catalog-backend-module-aws': patch
---
Added status and e-mail as labels to the AWS Account Resource
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/test-utils': minor
---
Added `components` option to `TestAppOptions`, which will be forwarded as the `components` option to `createApp`.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-kubernetes-backend': patch
'@backstage/plugin-kubernetes-node': patch
---
Enabling authentication to kubernetes clusters with mTLS x509 client certs
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-kubernetes-backend': patch
---
Custom per-cluster auth metadata (mainly for use with custom `AuthenticationStrategy` implementations) can now be specified in the `authMetadata` property of clusters in the app-config.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-linguist': patch
---
Added alpha support for the New Frontend System (Declarative Integration)
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-compat-api': patch
---
Plugins converted by `convertLegacyApp` now have their `routes` and `externalRoutes` included as well, allowing them to be used to bind external routes in configuration.
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-azure-devops': patch
'@backstage/plugin-devtools': patch
'@backstage/plugin-linguist': patch
---
Updated imports from named to default imports to help with the Material UI v4 to v5 migration
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/eslint-plugin': patch
---
Added new `@backstage/no-top-level-material-ui-4-imports` rule that forbids top level imports from Material UI v4 packages
+14 -7
View File
@@ -13,29 +13,33 @@ body:
required: true
attributes:
label: '📜 Description'
description: 'A clear and concise description of what the bug is. It bugs out when ...'
description: 'A clear and concise description of what the bug is.'
placeholder: 'It bugs out when ...'
- type: textarea
id: expected-behavior
validations:
required: true
attributes:
label: '👍 Expected behavior'
description: 'What did you think should happen? It should ...'
description: 'What did you think should happen?'
placeholder: 'It should ...'
- type: textarea
id: actual-behavior
validations:
required: true
attributes:
label: '👎 Actual Behavior with Screenshots'
description: 'What did actually happen? Add screenshots, if applicable. It actually ...'
description: 'What did actually happen? Add screenshots, if applicable.'
placeholder: 'It actually ...'
- type: textarea
id: steps-to-reproduce
validations:
required: true
attributes:
label: '👟 Reproduction steps'
description: 'How do you trigger this bug? Please walk us through it step by step. Provide a link to a live example, or an unambiguous set of steps to reproduce this bug. Include code or configuration to reproduce, if relevant.'
placeholder: "\n
description: 'How do you trigger this bug? Please walk us through it step by step.'
placeholder:
"Provide a link to a live example, or an unambiguous set of steps to reproduce this bug. Include code or configuration to reproduce, if relevant.\n
1. Go to '...'\n
2. Click on '....'\n
3. Scroll down to '....'"
@@ -45,14 +49,17 @@ body:
required: false
attributes:
label: '📃 Provide the context for the Bug.'
description: 'How has this issue affected you? What are you trying to accomplish? Providing context (e.g. links to configuration settings, stack trace or log data) helps us come up with a solution that is most useful in the real world.'
description: 'How has this issue affected you? What are you trying to accomplish?'
placeholder: 'Providing context (e.g. links to configuration settings, stack trace or log data) helps us come up with a solution that is most useful in the real world.'
- type: textarea
id: environment
validations:
required: false
attributes:
label: '🖥️ Your Environment'
description: 'Always provide output of `yarn backstage-cli info`; provide browser information when applicable. Include as many relevant details about the environment you experienced the bug in.'
description: 'Provide Browser Information
Provide Output of `yarn backstage-cli info`'
placeholder: 'Include as many relevant details about the environment you experienced the bug in.'
- type: checkboxes
id: no-duplicate-issues
attributes:
+4 -1
View File
@@ -14,6 +14,7 @@ body:
attributes:
label: '🔖 Feature description'
description: 'A clear and concise description of what the feature is.'
placeholder: 'You should add ...'
- type: textarea
id: context
validations:
@@ -21,11 +22,13 @@ body:
attributes:
label: '🎤 Context'
description: 'Please explain why this feature should be implemented and how it would be used. Add examples, if applicable.'
placeholder: 'In my use-case, ...'
- type: textarea
id: implementation
attributes:
label: '✌️ Possible Implementation'
description: 'A clear and concise description of what you want to happen. Not obligatory, but ideas as to the implementation of the addition or change.'
description: 'A clear and concise description of what you want to happen.'
placeholder: 'Not obligatory, but ideas as to the implementation of the addition or change'
- type: checkboxes
id: no-duplicate-issues
attributes:
+4 -1
View File
@@ -14,16 +14,19 @@ body:
attributes:
label: '🔖 Summary'
description: 'Provide a general summary of the plugin and how it should work'
placeholder: 'You should add ...'
- type: textarea
id: website
attributes:
label: '🌐 Project website (if applicable)'
description: 'Add a link to the open source project or product this plugin will integrate with, if existing'
placeholder: 'Website Link is ...'
- type: textarea
id: context
attributes:
label: '✌️ Context'
description: 'A clear and concise description about the Plugin and any additional context.'
description: 'A clear and concise description about the Plugin.'
placeholder: 'Providing additional context'
- type: checkboxes
id: no-duplicate-issues
attributes:
+8 -4
View File
@@ -13,24 +13,28 @@ body:
required: true
attributes:
label: '🔖 Need'
description: "Let us know why are you proposing this change. The problem we're trying to address and the benefits/impact we expect to get from this are ..."
description: 'Let us know why are you proposing this change'
placeholder: 'The problem were trying to address and the benefits/impact we expect to get from this are ...'
- type: textarea
id: proposal
validations:
required: true
attributes:
label: '🎉 Proposal'
description: 'Describe the proposal in as much detail as needed for reviewers to give concrete feedback. Take special care in this section to describe any implications on data privacy or security.'
description: 'Describe the proposal in as much detail as needed for reviewers to give concrete feedback.'
placeholder: 'Take special care in this section to describe any implications on data privacy or security.'
- type: textarea
id: alternatives
attributes:
label: '〽️ Alternatives'
description: 'What alternatives to the proposed solution were considered? What criteria/data was used to discard these?'
description: 'What alternatives to the proposed solution were considered?'
placeholder: 'What criteria/data was used to discard these?'
- type: textarea
id: risk
attributes:
label: '❌ Risks'
description: 'What other things happening could conflict or compete (for example for resources) with the proposal? What risk are there and how do we plan to handle them?'
description: 'What other things happening could conflict or compete (for example for resources) with the proposal?'
placeholder: 'What risk are there and how do we plan to handle them?'
- type: checkboxes
id: no-duplicate-issues
attributes:
+8 -5
View File
@@ -6,34 +6,37 @@ body:
- type: markdown
attributes:
value: |
We value your time and efforts to submit this UX Component form. 🙏
We value your time and efforts to submit this RFC form. 🙏
- type: textarea
id: ux-general
validations:
required: true
attributes:
label: '🔖 General'
description: |
'Write a nice note to the community requesting the creation of a new component. Include an image of your component. Bonus points for a GIF!'
description: 'Write a nice note to the community requesting the creation of a new component.'
placeholder: 'Include an image of your component. Bonus points for a GIF!'
- type: textarea
id: usage
validations:
required: true
attributes:
label: '💻 Usage'
description: "Tell us what's the point of this component/pattern is. How does it help? How should it work? Any rules?"
description: "Tell us what's the point of this component/pattern is."
placeholder: 'How does it help? How should it work? Any rules?'
- type: textarea
id: specs
validations:
required: true
attributes:
label: '📐 Specs'
description: "Include images that detail the redlines for your component. Once we get our Figma workspace set up, we'll be posting the Figma files rather than doing specs by hand."
description: 'Include images that detail the redlines for your component.'
placeholder: "Once we get our Figma workspace set up, we'll be posting the Figma files rather than doing specs by hand."
- type: textarea
id: future
attributes:
label: '🔮 Future'
description: 'List out any upcoming, exciting functionality for this component.'
placeholder: 'The component will have ...'
- type: checkboxes
id: no-duplicate-issues
attributes:
@@ -312,6 +312,8 @@ rebase
rebasing
Recharts
Redash
renderer
renderers
replicasets
repo
Repo
@@ -463,4 +465,4 @@ zod
Zolotusky
zoomable
zsh
scrollable
scrollable
+1
View File
@@ -6,6 +6,7 @@ on:
pull_request:
paths:
- 'microsite/**'
- 'beps/**'
permissions:
contents: read
+2 -1
View File
@@ -4,6 +4,7 @@ on:
pull_request:
paths-ignore:
- 'microsite/**'
- 'beps/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -215,7 +216,7 @@ jobs:
run: yarn backstage-cli repo lint --since origin/master
- name: test changed packages
run: yarn backstage-cli repo test --maxWorkers=2 --workerIdleMemoryLimit=1300M --since origin/master
run: yarn backstage-cli repo test --maxWorkers=3 --workerIdleMemoryLimit=1300M --since origin/master
env:
BACKSTAGE_TEST_DISABLE_DOCKER: 1
BACKSTAGE_TEST_DATABASE_POSTGRES16_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres16.ports[5432] }}
+1 -1
View File
@@ -98,7 +98,7 @@ jobs:
- name: test (and upload coverage)
run: |
yarn backstage-cli repo test --maxWorkers=2 --workerIdleMemoryLimit=1300M --coverage
yarn backstage-cli repo test --maxWorkers=3 --workerIdleMemoryLimit=1300M --coverage
bash <(curl -s https://codecov.io/bash)
# Upload code coverage for some specific flags. Also see .codecov.yml
bash <(curl -s https://codecov.io/bash) -f packages/core-app-api/coverage/* -F core-app-api
+1 -1
View File
@@ -66,6 +66,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
uses: github/codeql-action/upload-sarif@1500a131381b66de0c52ac28abb13cd79f4b7ecc # v2.22.12
uses: github/codeql-action/upload-sarif@4759df8df70c5ebe7042c3029bbace20eee13edd # v2.23.1
with:
sarif_file: results.sarif
@@ -29,7 +29,7 @@ jobs:
cache-prefix: ${{ runner.os }}-v18.x
- name: Create Snyk report
uses: snyk/actions/node@3e2680e8df93a24b52d119b1305fb7cedc60ceae # master
uses: snyk/actions/node@1d672a455ab3339ef0a0021e1ec809165ee12fad # master
continue-on-error: true # Snyk CLI exits with error when vulnerabilities are found
with:
args: >
+3 -3
View File
@@ -31,7 +31,7 @@ jobs:
- uses: actions/checkout@v4.1.1
- name: Monitor and Synchronize Snyk Policies
uses: snyk/actions/node@3e2680e8df93a24b52d119b1305fb7cedc60ceae # master
uses: snyk/actions/node@1d672a455ab3339ef0a0021e1ec809165ee12fad # 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@3e2680e8df93a24b52d119b1305fb7cedc60ceae # master
uses: snyk/actions/node@1d672a455ab3339ef0a0021e1ec809165ee12fad # 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@v2.22.12
uses: github/codeql-action/upload-sarif@v2.23.1
with:
sarif_file: snyk.sarif
+2 -1
View File
@@ -7,6 +7,7 @@ on:
- 'contrib/**'
- 'docs/**'
- 'microsite/**'
- 'beps/**'
- 'scripts/**'
- 'storybook/**'
- '**/*.test.*'
@@ -60,7 +61,7 @@ jobs:
- name: Docker metadata
id: meta
uses: docker/metadata-action@e6428a5c4e294a61438ed7f43155db912025b6b3 # v5.2.0
uses: docker/metadata-action@dbef88086f6cef02e264edb7dbf63250c17cef6c # v5.5.0
with:
images: registry.uffizzi.com/${{ env.UUID_TAG_APP }}
tags: type=raw,value=60d
+33 -36
View File
@@ -12,16 +12,23 @@ jobs:
name: Cache Manifests File
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
permissions:
# "If you specify the access for any of these scopes, all of those that are not specified are set to none."
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
actions: read # Access cache
outputs:
manifests-cache-key: ${{ env.MANIFESTS_FILE_HASH }}
git-ref: ${{ env.GIT_REF }}
pr-number: ${{ env.PR_NUMBER }}
action: ${{ env.ACTION }}
manifests-cache-key: ${{ steps.hash.outputs.MANIFESTS_FILE_HASH }}
git-ref: ${{ steps.event.outputs.GIT_REF }}
pr-number: ${{ steps.event.outputs.PR_NUMBER }}
action: ${{ steps.event.outputs.ACTION }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
with:
egress-policy: audit
disable-sudo: true
egress-policy: block
allowed-endpoints: >
api.github.com:443
- name: 'Download artifacts'
# Fetch output (zip archive) from the workflow run that triggered this workflow.
@@ -48,59 +55,50 @@ jobs:
let fs = require('fs');
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/preview-spec.zip`, Buffer.from(download.data));
- name: 'Unzip artifact'
run: unzip preview-spec.zip
- name: 'Accept event from first stage'
run: unzip preview-spec.zip event.json
- name: Read Event into ENV
id: event
run: |
echo 'EVENT_JSON<<EOF' >> $GITHUB_ENV
cat event.json >> $GITHUB_ENV
echo -e '\nEOF' >> $GITHUB_ENV
echo PR_NUMBER=$(jq '.number | tonumber' < event.json) >> $GITHUB_OUTPUT
echo ACTION=$(jq --raw-output '.action | tostring | [scan("\\w+")][0]' < event.json) >> $GITHUB_OUTPUT
echo GIT_REF=$(jq --raw-output '.pull_request.head.sha | tostring | [scan("\\w+")][0]' < event.json) >> $GITHUB_OUTPUT
- name: Hash Rendered Manifests File
id: hash
# If the previous workflow was triggered by a PR close event, we will not have a manifests file artifact.
if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }}
if: ${{ steps.event.outputs.ACTION != 'closed' }}
run: |
unzip preview-spec.zip manifests.rendered.yml
ls
echo "MANIFESTS_FILE_HASH=$(md5sum manifests.rendered.yml | awk '{ print $1 }')" >> $GITHUB_ENV
echo "MANIFESTS_FILE_HASH=$(md5sum manifests.rendered.yml | awk '{ print $1 }')" >> $GITHUB_OUTPUT
- name: Cache Manifests File
if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }}
uses: actions/cache@v3.3.2
if: ${{ steps.event.outputs.ACTION != 'closed' }}
uses: actions/cache@v3.3.3
with:
path: manifests.rendered.yml
key: ${{ env.MANIFESTS_FILE_HASH }}
- name: Read PR Number From Event Object
id: pr
run: echo "PR_NUMBER=${{ fromJSON(env.EVENT_JSON).number }}" >> $GITHUB_ENV
- name: Read Event Type from Event Object
id: action
run: echo "ACTION=${{ fromJSON(env.EVENT_JSON).action }}" >> $GITHUB_ENV
- name: Read Git Ref From Event Object
id: ref
run: echo "GIT_REF=${{ fromJSON(env.EVENT_JSON).pull_request.head.sha }}" >> $GITHUB_ENV
key: ${{ steps.hash.outputs.MANIFESTS_FILE_HASH }}
- name: DEBUG - Print Job Outputs
if: ${{ runner.debug }}
run: |
echo "PR number: ${{ env.PR_NUMBER }}"
echo "Git Ref: ${{ env.GIT_REF }}"
echo "Manifests file hash: ${{ env.MANIFESTS_FILE_HASH }}"
echo "PR number: ${{ steps.event.outputs.PR_NUMBER }}"
echo "Git Ref: ${{ steps.event.outputs.GIT_REF }}"
echo "Action: ${{ steps.event.outputs.ACTION }}"
echo "Manifests file hash: ${{ steps.hash.outputs.MANIFESTS_FILE_HASH }}"
cat event.json
deploy-uffizzi-preview:
permissions:
contents: read
pull-requests: write
id-token: write
name: Deploy to Uffizzi Virtual Cluster
needs:
- cache-manifests-file
if: ${{ github.event.workflow_run.conclusion == 'success' && needs.cache-manifests-file.outputs.action != 'closed' }}
permissions:
contents: read
pull-requests: write
id-token: write
runs-on: ubuntu-latest
steps:
- name: Checkout
@@ -126,7 +124,7 @@ jobs:
body: |
## Uffizzi Ephemeral Environment - Virtual Cluster
:cloud: deploying ...
:cloud: deploying cluster `pr-${{ needs.cache-manifests-file.outputs.pr-number }}`
:gear: Updating now by workflow run [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}).
@@ -142,7 +140,6 @@ jobs:
- name: Fetch cached Manifests File
id: cache
# if: ${{ contains(fromJSON('["create", "update"]'), env.UFFIZZI_ACTION) }}
uses: actions/cache@v3
with:
path: manifests.rendered.yml
+3 -3
View File
@@ -55,7 +55,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2.22.12
uses: github/codeql-action/init@v2.23.1
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@v2.22.12
uses: github/codeql-action/autobuild@v2.23.1
# ️ 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@v2.22.12
uses: github/codeql-action/analyze@v2.23.1
@@ -30,5 +30,6 @@ jobs:
with:
# This also contains --config=.github/vale/config.ini ... :/
files: '${{ steps.generate.outputs.args }}'
version: latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -9,6 +9,7 @@ on:
- 'contrib/**'
- 'docs/**'
- 'microsite/**'
- 'beps/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
+1
View File
@@ -7,6 +7,7 @@ on:
- 'contrib/**'
- 'docs/**'
- 'microsite/**'
- 'beps/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -7,6 +7,7 @@ on:
paths-ignore:
- '.github/workflows/verify_microsite.yml'
- 'microsite/**'
- 'beps/**'
- 'mkdocs.yml'
- 'docs/**'
+1
View File
@@ -5,6 +5,7 @@ on:
paths:
- '.github/workflows/verify_microsite.yml'
- 'microsite/**'
- 'beps/**'
- 'mkdocs.yml'
- 'docs/**'
+1 -1
View File
@@ -51,7 +51,7 @@ jobs:
- run: yarn build-storybook
- uses: chromaui/action@80bf5911f28005ed208f15b7268843b79ca0e23a # v1
- uses: chromaui/action@7fb6b0407c69171ce521d08355a825958a5ef81a # v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
# projectToken intentionally shared to allow collaborators to run Chromatic on forks
+1 -1
View File
@@ -55,7 +55,7 @@ jobs:
run: yarn lint:type-deps
- name: test
run: yarn backstage-cli repo test --maxWorkers=2 --workerIdleMemoryLimit=1300M
run: yarn backstage-cli repo test --maxWorkers=3 --workerIdleMemoryLimit=1300M
env:
BACKSTAGE_TEST_DISABLE_DOCKER: 1
+1 -1
View File
@@ -386,7 +386,7 @@ Here are a few things that can help as you go through the review process:
- PRs get automatically assigned so you don't need to ping people, they will be notified and have a process of their own for this
- If you are waiting for a review or mid-review and your PR goes stale one of the easiest ways to clear the stale bot is by simply rebasing your PR
- There are times where you might run into conflict with the `yarn.lock` during a rebase, to help with that make sure your `master` branch is up to date and then in your branch run `git checkout master yarn.lock` and then run `yarn install`, this will get you a conflict free `yarn.lock` file you can commit
- If Vale finds issues with your documentation but it's a code reference you can fix it by putting backticks (`) around it. Now if it is a special word or maybe a name there are two ways you can fix that by adding it to the list of accepted words in the [accept.txt file](https://github.com/backstage/backstage/blob/master/.github/vale/Vocab/Backstage/accept.txt) and them committing that change
- If Vale finds issues with your documentation but it's a code reference you can fix it by putting backticks (`) around it. Now if it is a special word or maybe a name there are two ways you can fix that by adding it to the list of accepted words in the [accept.txt file](https://github.com/backstage/backstage/blob/master/.github/vale/config/vocabularies/Backstage/accept.txt) and them committing that change
### Merging to Master
+2 -2
View File
@@ -22,10 +22,10 @@ Backstage 는 모든 인프라 도구, 서비스 및 문서를 통합하여 처
![software-catalog](docs/assets/header.png)
Backstage 다음을 포함합니다:
Backstage 다음을 포함합니다:
- [Backstage Software Catalog](https://backstage.io/docs/features/software-catalog/) 마이크로 서비스, 라이브러리, 데이터 파이프라인, 웹 사이트, ML 모델 등 모든 소프트웨어 관리
- [Backstage Software Templates](https://backstage.io/docs/features/software-templates/) 새로운 프로젝트를 신속하게 시작하고 조직의 모 사례에따라 도구를 표준화
- [Backstage Software Templates](https://backstage.io/docs/features/software-templates/) 새로운 프로젝트를 신속하게 시작하고 조직의 모 사례에따라 도구를 표준화
- [Backstage TechDocs](https://backstage.io/docs/features/techdocs/) "docs like code" 접근 방식을 사용하여 기술 문서를 쉽게 작성, 유지 관리, 검색 및 사용
- [open source plugins](https://github.com/backstage/backstage/tree/master/plugins) Backstage의 사용자 정의 가능성과 기능을 확장
+1 -1
View File
@@ -57,7 +57,7 @@ Backstage 的文档包括:
- [参与贡献 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.: 常问问题
- [行为准则](CODE_OF_CONDUCT.md) - 这是我们的事方式
- [行为准则](CODE_OF_CONDUCT.md) - 这是我们的事方式
- [采纳者](ADOPTERS.md) - 已经在使用 Backstage 的公司
- [博客](https://backstage.io/blog/) - 公告和更新
- [通讯](https://spoti.fi/backstagenewsletter) - 订阅我们的电子邮件通讯
+255
View File
@@ -0,0 +1,255 @@
---
title: Backstage Notifications System
status: provisional
authors:
- Rugvip
project-areas:
- core
creation-date: 2023-01-12
---
# BEP: Backstage Notifications System
[**Discussion Issue**](https://github.com/backstage/backstage/issues/22213)
- [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
The Backstage Notifications System provides a way for Backstage plugins to send notifications to users. The notifications are displayed in the Backstage frontend UI, but future extensions can allow them to be sent via external channels such as email. The system includes a push mechanism to make sure that users receive notifications immediately, as well as persistence of notification history and read status.
## Motivation
Support for notifications is an old and common features request. See [#639](https://github.com/backstage/backstage/issues/639) and [#10652](https://github.com/backstage/backstage/issues/10652). There are also initiatives to implement notifications in [#20312](https://github.com/backstage/backstage/issues/20312) as well as the underlying infrastructure pieces, for example [#17997](https://github.com/backstage/backstage/issues/17997).
This proposal assumes that further motivation for a notifications system as a whole is not needed. The focus is instead on the specific goals of an initial notifications system, and generally aiming to address the need to coordinate this work.
### Goals
We aim to build a system that would provide support for the following type of notifications, as listed in [#10652](https://github.com/backstage/backstage/issues/10652):
- System-wide announcements or alerts
- Notifications for component owners (e.g. build failure, successful deployment, 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 given notification might apply to an entity and the owning team)
Initially we only target a single way to send notifications: via a REST API call to the notifications backend, which in turn also provides an accompanying [backend service](https://backstage.io/docs/backend-system/architecture/services).
The notifications system will need to support scaled deployments, where multiple backend instances may be deployed.
In the future we aim to provide an extensible system that can be customized to send notifications to users via other channels. Notifications can be captured and processed in-band in order to trigger custom behavior such as sending an email. The goal is for this to be possible to build, but not necessarily part of the initial implementation.
### Non-Goals
The following features are out of scope for this BEP:
- Building out specific support for channels such as email, Slack, and other external channels.
- Providing a fallback mechanisms when a notification cannot be delivered to a user. For example, if a user has not logged in for a long time, we will not attempt to send an email instead.
## Proposal
The notifications system is implemented through two net-new components, the `notifications` plugin, and the `signal` plugin.
The `notifications` plugin in its simplest form provides the following:
- A backend API for sending, reading, and interacting with notifications.
- Persistent storage of notification state.
- A frontend UI for viewing notifications.
The `signal` plugin provides the following:
- A backend API for pushing lightweight signals to online users.
- A connection between the frontend and backend that can be used to push messages to the client.
- A frontend API that lets plugins to subscribe to specific signals.
### Signals Plugin
In the backend the signal plugin implements a general purpose message bus for sending signals from backend plugins to connected users. It relies on the `EventBroker` from the [events plugin](https://github.com/backstage/backstage/blob/master/plugins/events-backend/README.md) for the actual message passing in the backend. In order to support scaled deployments, each signal backend instance has a separate subscription to the event broker so that each instance receives all events. It is then up to each backend instance to filter out events that are not relevant to it. For this reason, signals should be kept lightweight and not contain unnecessary data.
In the frontend the signal plugin has a persistent connection to the signal backend. This is initially implemented as a WebSocket connection, but could in the future also receive fallback mechanisms such as Server Sent Events or long polling. It is important that this connection is authenticated as we will be routing signals to specific users. The exact implementation of the authentication is not part of this proposal, but it should use whatever the outcome of the discussion in issue [#19581](https://github.com/backstage/backstage/issues/19581) is.
In order to route signals from the sender to the intended receiver the signal plugin uses the concept of signaling channels. They are just like the topics on the message bus, but we use separate terminology in order to avoid confusion. They also sit one layer beneath the even topic in the protocol stack, where the signal channel is communicated as part of the event payload. In the frontend, the signal plugin exposes an API to subscribe to a signal channel. Each time the user receives a signal on the specified channel, a listener is called with the signal payload.
### Notifications Plugin
The role of the notifications plugin is to manage the lifecycle of notifications. The backend plugin provides an API for other backends to send notifications, as well as an accompanying [backend service](https://backstage.io/docs/backend-system/architecture/services). It also provides a separate API for the frontend plugin to read notifications for an individual user and manage the read status of notifications.
The notification backend stores notification using the [database service](https://backstage.io/docs/backend-system/core-services/index#database). In particular it needs to store the following information for each notification:
- ID
- Payload
- Receivers
- Read status by user
The receivers is **not** a list of users, but rather a filter that describes who should receive the notification. It must be possible to evaluate this filter in a database query, so that we can efficiently fetch all notifications for a given user. The same filter will also be used by the signal backend to determine which users should receive a signal.
The notification backend does not provide any new permissions, since creating notifications can only be done by other backend plugins, while reading notifications can only be done by the authenticated user. It is possible that we want to add a permissions for reading notifications, in particular for admin and impersonation use cases, but that is not part of this proposal or the initial implementation.
The notification frontend plugin provides a UI for viewing notifications, which in the initial implementation can be as simple as needed. The only requirement is that a user is able to view recent notifications and distinguish between read and unread notifications. A notification as marked as read once it has been interacted with. The frontend plugin also subscribes to the notifications signal channel and alerts the user when a new notification is received.
### Architecture Overview
The diagram below show an overview of the notifications system architecture and the interaction between the different components.
![notifications system architecture diagram](./notifications-architecture.drawio.svg)
The components that are presented with dashed lines are not directly part of this proposal, but included to be considered as future extensions.
Let's walk through the process of sending of a notification to a user:
1. A backend plugin that wants to send a notification to a user uses the `NotificationService`, which in turn makes a POST request to the notification backend. The request contains a filter to select the target users, as well as the notification payload.
1b. As the notification backend handles the request it could optionally invoke a series of notification processors. These processors can be used to transform the notification payload, send the notification through other channels, and decide whether the regular notification flow should continue or not. This step is not in scope for this proposal.
2. The notification backend stores the received notification in its database. At this point the notification is available from the notification backend's read API.
3. The notification backend published an event to the event bus on the `'signal'` topic. This event payload contains the signaling channel, in this case `'notifications'`, as well as the target user ID and the notification ID, but not the notification payload.
4. Each instance of the signal backend plugin subscribes to the `'signal'` topic and receives the event.
5. Each signal backend instance has a set of push channels set up to online users. The incoming event is filtered based on the target user ID and the signaling is pushed through all connections with a matching user ID.
6. The signal frontend plugin receives the signal and forwards it to all subscribers of the target channel. In this case, the notifications frontend plugin, who is subscribed to the `'notifications'` channel.
7. The notifications frontend plugin receives the signal with the notification ID. It then makes a request to the notification backend to fetch the notification payload and then displays the notification to the user.
7b. Rather than rendering the notification directly, the notifications frontend plugin leaves that to an extension installed by the frontend part of the backend plugin that sent the notification. This allows for a more customized notification experience which can also contain custom actions and data display. This step is not in scope for this proposal.
## Design Details
### Backend Services
The following backend service interfaces are added as part of this proposal.
#### `NotificationService`
```ts
// TODO - We may want to add an additional wrapping here with interfaces for Notification and NotificationParameters
interface SendNotificationRequest {
receivers: SignalReceivers;
/* TODO - please contribute :) */
}
interface SendNotificationResponse {
id: string;
receivers: SignalReceivers;
/* TODO - please contribute :) */
}
interface NotificationService {
sendNotification(
request: SendNotificationRequest,
): Promise<SendNotificationResponse>;
}
```
#### `SignalService`
```ts
type SignalReceivers =
| {
type: 'entity';
entityRef: string; // typically a user or group entity
}
| {
type: 'broadcast'; // all users
};
interface Signal {
channel: string;
receivers: SignalReceivers;
payload: JsonObject;
}
interface SignalService {
signal(signal: Signal): Promise<void>;
}
```
### Frontend API
The following frontend API is added as part of this proposal.
#### `SignalApi`
> TODO - we likely need a slightly different approach here, since APIs are lazy loaded.
```ts
interface SignalSubscriber {
unsubscribe(): void;
}
interface SignalApi {
subscribe(
channel: string,
listener: (payload: JsonObject) => void,
): SignalSubscriber;
}
```
> TODO: Add payload formats and API details
<!--
- A notification can be sent to any entity
- If the entity is an user, the notification is sent to that user
- If the entity is a group, the notification is sent to all users that are members of that group
- In other cases, the notification is sent to to the user or group in the `spec.owner` field
- Notification frontend shows users their own notifications in its own page and the number of unread notifications
in the main menu item
- Notifications can be set to `read` by the user and they are filtered out of the main view
- Notifications can be saved for later use by the user
- Each notification must have title, description and link
- Each notification can also have icon or image as an URL
- Notification frontend utilizes [Web Notification API](https://developer.mozilla.org/en-US/docs/Web/API/Notification)
to notify user for new notifications
-->
## Release Plan
The notification and signal plugins are released as two new plugins in the Backstage ecosystem. They will both start out in an experimental state.
For the notification plugin to reach a stable release we much reach the following:
- A stable notifications payload format.
- A stable notifications receiver filter format.
- The event broker must have at least one implementation that supports scaled deployments.
For the signal plugin to reach a stable release we much reach the following:
- A stable signal receiver filter format.
- A stable signal channel API in the frontend.
If any changes are required to the frontend framework to facilitate the implementation of notifications or signals, these will be released as experimental alpha features. They will stay in alpha until they are deemed stable enough, which must happen before a stable release of the notifications system.
## Dependencies
Since the signal plugin relies on the event broker for communication, it is a dependency for the notifications system as a whole. The event broker does not currently implement any transport for scaled deployments, which is a requirement for scaled deployments of the notification system.
## Alternatives
### Signal Plugin Separation
One primary consideration for the notifications system was whether the notification plugin should set up its own frontend push connection rather than relying on the signal plugin. The signal plugin separation is done for three primary reasons:
1. The use-case of sending signals from backend plugins to frontend plugins is not limited to only notifications, since not all signals should necessarily be displayed directly to the user. For example, the catalog backend may want to send a signal to the frontend when an entity is updated so that users that are currently viewing the entity in the frontend can trigger an update of the view.
2. We want to limit the number of WebSocket connections that the frontend needs to maintain. By using a separated signal abstraction it is easier to extend the implementation to support other messaging patterns in the future. For example, we may want to move the Scaffolder logs to be sent via the signal plugin instead. That is much more straightforward with a separate signal plugin compared to adding it to the notifications plugin.
3. The technical implementation of sending signals is sufficiently complex that it makes sense to separate out to its own solution in order to reduce the complexity of the notifications plugin. In particular when it comes to handling scaled deployments.
### User-to-User Notifications
The notification backend API for sending notifications is only available to other backend plugins, and is not accessible to end users in the frontend. If this were to be added it would likely be a separate backend module that you can install alongside the notification backend. That is unless the permission framework can be extended to support default policy handlers so that we can reject the permission to send user notifications by default.
There are a few reasons we don't want to allow user-to-user notifications out of the box:
- To limit the possibility of spam. Even if well-intended, a notification overload can quickly lead to a bad user experience and users disabling and ignoring notifications.
- From a security perspective it's beneficial to avoid user sent notifications since they could be used to trick users into clicking on malicious links, or other similar attacks.
- Our assumption is that this is a feature that most adopters will not want to use, and so by having it be disabled by default we can avoid additional configuration steps.
@@ -0,0 +1,537 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="671px" height="311px" viewBox="-0.5 -0.5 671 311" content="&lt;mxfile scale=&quot;1&quot; border=&quot;20&quot;&gt;&lt;diagram id=&quot;2fh7CcgDxUdRYRy3FD-Z&quot; name=&quot;Page-1&quot;&gt;7Vxbk6I4FP41Vu0+tEVAUB9bu+192N3pWqdqZx4jREk1Agto6/76TSDckqhIA97W6RrJIdy+79xyEulp0/XuLYC+/YdnIaenKtaup730VHU40Mn/VLBPBCPDSASrAFuJCOSCOf4XMaHCpBtsobDUMfI8J8J+WWh6rovMqCSDQeB9lrstPad8VR+ukCCYm9ARpX9jK7ITqTZWcvlvCK9sdmXNYDsW0PxYBd7GZZfrqdos/iS71zA9Fesf2tDyPgsi7bWnTQPPi5Kt9W6KHIpsilpy3OzA3uy2A+RGVQ5IeQqjffroyCJIsKbrueRrYkdrh7QA2bRgaCOLNZaeGzHuCHHaJIxgED1T+PNjY9kMO/QEtA9yLa4HkRT373D0g273VZ01fxZ2veyKjT1riI/NkAi9TWCyB1OZHsFghVivwTCR0WcuHMewekPeGkXBnnQIkAMjvC1rB2RKtsr6ZYe+e5jciaowe3gCKgN6n+q5Uj5HclfssJwwslG4j1wU0yin1FDPpDRHfFgdcQJ0sE+O0tPmz/SEtJEfFrdKx72jAJMnQgETflGPFo5nfny3sVtSplhByebUc7wgxkFb6vRffLLA+0CFPUb8+YImpQ6gYU0ijwj3hQ4+VZDwiKKlHirVM97++f7G0f5kI7mDusrIkNpCZ8Oe+E8vwktsEgw8l+yZEIdJOBNUNvahsaOhZH/aOEJzH8YEfJKIU9bgQzRTvSrIk089+rcoiNDuKLOZrY84TAes/ZlHkhRnxS5EkYFyWBtKDuEI4Ok5zrf+oun39aPG35q9lqNLO8aricY7atp2K7M1qu2rq7vqW/OuEoLUSxGknfJfs4Cge+cOLAtunTgwAfE5XrnQuctYoVeIFEpLQKdG1arvOZXtl3xR0dMUHVKLvkYXfc3gUr4mdTYtE1I3bz9OZHf8AL1pgmpl2pldpk7yRKYNxkf7fz3THvyvPVW0x7hJ7eEjRePaM66rPWcMGzLtAUXdKajSAe259oRVombapYKIfjh9usNUlbcKWaLaVv5U12JAHYtRzrOY8xW9kv1UsgZDtIaLDd8MwRrenc0K04Hbj7scUDxpfEGvy+LT6Djc1+aAStWmNsDvdOAMzh7PXZdzqeMfKzkkSbUeNB6fD8378BoBOKaTGxXmfcQzqVzGmJ35xAzSuZmpKr/MwcS03H3UbFoKhjWDbHlQA44q0aFBjXLCDi6ZcgJJkfRihQvQTBX7MVi60vLFqYlCvnzR9EQhqD0AfUQdus4ixikdEsrdTeuQOFvzukXxHUwoB8G1JJ4N5JpcXiFJNEdt5ZliieGOcAXD0+OnQVvIisPVO0JW1S6HbJqlFpDt9/sCuORJojKCZURYFCrCykTQwSsSEl5Mgg9d1jShuGATOs9sxxpbFr2MlLIyqU1ocYWJxbbGodndPgjW2ZDuAlhrqoC1v1k4OLRpdpDEvqvEnV+T0gAPwlI2CQ+ymKg2wYO4amEReNAyYfhADGSI84tcu2BAkpVoM/b3OAxw5RhVwoDeFgNi9hKiYEuzbsXfUI/0MCzwk7uayMKwLRbGAgvv3+bfHwf8J5X3QpI4YLSE/uDcRRhN/pyi89JE+uuJUhGSZSTtV9a5gtSIo+9AYb1GWSF9pGJZYUdUP5lVn+/DCK0F2u9mSosbSXS6FFSs56A1xKTLLHSg+UG+IwTXYU+dUo2LzGsdZbTh5nhiqs7zNuLmxFD/F/E4kgLb/eLPhxmZZbQWZoYC/j3VcCjSoQ/dEgXGPxv6Y8KJmcD7TF3vavGLDhKryb9/jYFRKFZPYQwW7QtUfxfvSM9Dtlb0+z3wTBSG5JzsytQq44sn+8VA6DjYDw8RVjUmXlVJSwhCHZZhB2Ki9/b6SHmeMNYBIvhaSwaY+t4C+ECAvrLCw9BPfsO8xDsK2UmlXy5V05Qpt2UsDL0p5RYKtkMR4LYijC4WESWr428cYL5I2yW+Yk4LFncHsPBLTImLaA1hsSQoGRzeNsD8XFmX8Ir1PsmrDG4bXsDPLIw7xFdM8Y17w5dPILqEV8zgh/fnf/mpsU4RFvNjyWLCGweYn+eVVZrbAjgd6xxbrRDa0Keb5t7BtD6gnUZ6kQwcfl9kguyVOt82ETkNYvJkjPoy6I8NNf8bXP+gkUtKdJEy2TIIvQnKxIKayNnN2wTgXrXTYVZiiEnf1IauG78cawodZxGXLW9ojC4AL6Gn8pS8tHoMmuGCNPPXZiVV/vzNZNrrfw==&lt;/diagram&gt;&lt;/mxfile&gt;" style="background-color: rgb(255, 255, 255);">
<defs/>
<g>
<path d="M 230 120 L 230 80" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
<path d="M 290 120 L 290 110 Q 290 100 300 100 L 310 100 Q 320 100 320 94.06 L 320 88.12" fill="none" stroke="#666666" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 320 81.12 L 322.33 88.12 L 317.67 88.12 Z" fill="#666666" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/>
<rect x="200" y="120" width="120" height="40" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 140px; margin-left: 201px;">
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Notification Backend
</div>
</div>
</div>
</foreignObject>
<text x="260" y="144" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
Notification Backend
</text>
</switch>
</g>
<path d="M 200 260 L 148.12 260" fill="none" stroke="#666666" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
<path d="M 141.12 260 L 148.12 257.67 L 148.12 262.33 Z" fill="#666666" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 260 240 L 260 168.12" fill="none" stroke="#666666" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 260 161.12 L 262.33 168.12 L 257.67 168.12 Z" fill="#666666" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/>
<rect x="200" y="240" width="120" height="40" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 260px; margin-left: 201px;">
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Notification Frontend
</div>
</div>
</div>
</foreignObject>
<text x="260" y="264" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
Notification Frontend
</text>
</switch>
</g>
<rect x="430" y="120" width="100" height="40" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 140px; margin-left: 431px;">
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Signal Backend
</div>
</div>
</div>
</foreignObject>
<text x="480" y="144" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
Signal Backend
</text>
</switch>
</g>
<path d="M 480 231.88 L 480 160" fill="none" stroke="#666666" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 480 238.88 L 477.67 231.88 L 482.33 231.88 Z" fill="#666666" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 480 240 L 480 210 Q 480 200 490 200 L 560 200 Q 570 200 570 190 L 570 160" fill="none" stroke="#666666" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 480 240 L 480 210 Q 480 200 490 200 L 620 200 Q 630 200 630 190 L 630 160" fill="none" stroke="#666666" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 430 260 L 328.12 260" fill="none" stroke="#666666" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 321.12 260 L 328.12 257.67 L 328.12 262.33 Z" fill="#666666" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/>
<rect x="430" y="240" width="100" height="40" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 260px; margin-left: 431px;">
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Signal Frontend
</div>
</div>
</div>
</foreignObject>
<text x="480" y="264" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
Signal Frontend
</text>
</switch>
</g>
<path d="M 140 140 L 191.88 140" fill="none" stroke="#666666" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 198.88 140 L 191.88 142.33 L 191.88 137.67 Z" fill="#666666" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/>
<rect x="20" y="120" width="120" height="40" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 140px; margin-left: 21px;">
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Plugin X Backend
</div>
</div>
</div>
</foreignObject>
<text x="80" y="144" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
Plugin X Backend
</text>
</switch>
</g>
<rect x="20" y="240" width="120" height="40" fill="#f5f5f5" stroke="#666666" stroke-dasharray="3 3" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 260px; margin-left: 21px;">
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Plugin X Frontend
</div>
</div>
</div>
</foreignObject>
<text x="80" y="264" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
Plugin X Frontend
</text>
</switch>
</g>
<path d="M 320 140 L 390 140 Q 400 140 400 130 L 400 70 Q 400 60 410 60 L 431.88 60" fill="none" stroke="#666666" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 438.88 60 L 431.88 62.33 L 431.88 57.67 Z" fill="#666666" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 480 80 L 480 111.88" fill="none" stroke="#666666" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 480 118.88 L 477.67 111.88 L 482.33 111.88 Z" fill="#666666" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 480 80 L 480 90 Q 480 100 490 100 L 560 100 Q 570 100 570 105.94 L 570 111.88" fill="none" stroke="#666666" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 570 118.88 L 567.67 111.88 L 572.33 111.88 Z" fill="#666666" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 480 80 L 480 90 Q 480 100 490 100 L 620 100 Q 630 100 630 105.94 L 630 111.88" fill="none" stroke="#666666" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 630 118.88 L 627.67 111.88 L 632.33 111.88 Z" fill="#666666" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/>
<rect x="440" y="40" width="80" height="40" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 60px; margin-left: 441px;">
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Event Broker
</div>
</div>
</div>
</foreignObject>
<text x="480" y="64" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
Event Broker
</text>
</switch>
</g>
<rect x="550" y="120" width="40" height="40" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
<rect x="610" y="120" width="40" height="40" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
<rect x="530" y="120" width="20" height="40" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 140px; margin-left: 531px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
...
</div>
</div>
</div>
</foreignObject>
<text x="540" y="144" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
...
</text>
</switch>
</g>
<rect x="590" y="120" width="20" height="40" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 140px; margin-left: 591px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
...
</div>
</div>
</div>
</foreignObject>
<text x="600" y="144" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
...
</text>
</switch>
</g>
<rect x="320" y="120" width="80" height="20" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 130px; margin-left: 321px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
publish event
</div>
</div>
</div>
</foreignObject>
<text x="360" y="133" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
publish event
</text>
</switch>
</g>
<rect x="520" y="80" width="80" height="20" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 90px; margin-left: 521px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
broadcast
</div>
</div>
</div>
</foreignObject>
<text x="560" y="93" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
broadcast
</text>
</switch>
</g>
<rect x="500" y="180" width="50" height="20" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 48px; height: 1px; padding-top: 190px; margin-left: 501px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
???
</div>
</div>
</div>
</foreignObject>
<text x="525" y="193" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
???
</text>
</switch>
</g>
<rect x="480" y="210" width="70" height="20" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 220px; margin-left: 481px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
server push
</div>
</div>
</div>
</foreignObject>
<text x="515" y="223" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
server push
</text>
</switch>
</g>
<rect x="140" y="120" width="60" height="20" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 130px; margin-left: 141px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
POST
</div>
</div>
</div>
</foreignObject>
<text x="170" y="133" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
POST
</text>
</switch>
</g>
<path d="M 190 60 L 158.12 60" fill="none" stroke="#666666" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
<path d="M 151.12 60 L 158.12 57.67 L 158.12 62.33 Z" fill="#666666" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/>
<rect x="30" y="40" width="120" height="40" fill="#f5f5f5" stroke="#666666" stroke-dasharray="3 3" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 60px; margin-left: 31px;">
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
External System
</div>
</div>
</div>
</foreignObject>
<text x="90" y="64" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
External System
</text>
</switch>
</g>
<rect x="30" y="20" width="120" height="20" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 30px; margin-left: 31px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
email/slack/teams, etc.
</div>
</div>
</div>
</foreignObject>
<text x="90" y="33" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
email/slack/teams, etc.
</text>
</switch>
</g>
<rect x="140" y="240" width="60" height="20" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 250px; margin-left: 141px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Render
</div>
</div>
</div>
</foreignObject>
<text x="170" y="253" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
Render
</text>
</switch>
</g>
<ellipse cx="230" cy="60" rx="40" ry="20" fill="#f5f5f5" stroke="#666666" stroke-dasharray="3 3" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 60px; margin-left: 191px;">
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<span style="color: rgb(51, 51, 51); font-size: 12px;">
Processor
</span>
</div>
</div>
</div>
</foreignObject>
<text x="230" y="63" fill="#333333" font-family="Helvetica" font-size="10px" text-anchor="middle">
Processor
</text>
</switch>
</g>
<rect x="260" y="190" width="30" height="20" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 28px; height: 1px; padding-top: 200px; margin-left: 261px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
GET
</div>
</div>
</div>
</foreignObject>
<text x="275" y="203" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
GET
</text>
</switch>
</g>
<ellipse cx="160" cy="160" rx="10" ry="10" fill="#fff2cc" stroke="#d6b656" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 160px; margin-left: 151px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
1
</div>
</div>
</div>
</foreignObject>
<text x="160" y="163" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
1
</text>
</switch>
</g>
<ellipse cx="340" cy="160" rx="10" ry="10" fill="#fff2cc" stroke="#d6b656" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 160px; margin-left: 331px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
3
</div>
</div>
</div>
</foreignObject>
<text x="340" y="163" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
3
</text>
</switch>
</g>
<ellipse cx="210" cy="100" rx="10" ry="10" fill="#fff2cc" stroke="#d6b656" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 100px; margin-left: 201px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
1b
</div>
</div>
</div>
</foreignObject>
<text x="210" y="103" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
1b
</text>
</switch>
</g>
<ellipse cx="460" cy="100" rx="10" ry="10" fill="#fff2cc" stroke="#d6b656" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 100px; margin-left: 451px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
4
</div>
</div>
</div>
</foreignObject>
<text x="460" y="103" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
4
</text>
</switch>
</g>
<ellipse cx="500" cy="180" rx="10" ry="10" fill="#fff2cc" stroke="#d6b656" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 180px; margin-left: 491px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
5
</div>
</div>
</div>
</foreignObject>
<text x="500" y="183" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
5
</text>
</switch>
</g>
<ellipse cx="410" cy="280" rx="10" ry="10" fill="#fff2cc" stroke="#d6b656" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 280px; margin-left: 401px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
6
</div>
</div>
</div>
</foreignObject>
<text x="410" y="283" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
6
</text>
</switch>
</g>
<ellipse cx="180" cy="280" rx="10" ry="10" fill="#fff2cc" stroke="#d6b656" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 280px; margin-left: 171px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
7b
</div>
</div>
</div>
</foreignObject>
<text x="180" y="283" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
7b
</text>
</switch>
</g>
<ellipse cx="240" cy="220" rx="10" ry="10" fill="#fff2cc" stroke="#d6b656" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 220px; margin-left: 231px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
7
</div>
</div>
</div>
</foreignObject>
<text x="240" y="223" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
7
</text>
</switch>
</g>
<path d="M 300 34.96 C 300 32.22 308.95 30 320 30 C 325.3 30 330.39 30.52 334.14 31.45 C 337.89 32.38 340 33.65 340 34.96 L 340 75.04 C 340 76.35 337.89 77.62 334.14 78.55 C 330.39 79.48 325.3 80 320 80 C 314.7 80 309.61 79.48 305.86 78.55 C 302.11 77.62 300 76.35 300 75.04 Z" fill="#f5f5f5" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 340 34.96 C 340 36.28 337.89 37.54 334.14 38.47 C 330.39 39.4 325.3 39.93 320 39.93 C 314.7 39.93 309.61 39.4 305.86 38.47 C 302.11 37.54 300 36.28 300 34.96" fill="none" stroke="#666666" stroke-miterlimit="10" pointer-events="all"/>
<ellipse cx="275" cy="100" rx="10" ry="10" fill="#fff2cc" stroke="#d6b656" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 100px; margin-left: 266px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
2
</div>
</div>
</div>
</foreignObject>
<text x="275" y="103" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
2
</text>
</switch>
</g>
<rect x="320" y="240" width="110" height="20" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 250px; margin-left: 321px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Channel Callback
</div>
</div>
</div>
</foreignObject>
<text x="375" y="253" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
Channel Callback
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 44 KiB

@@ -0,0 +1,492 @@
---
title: Dynamic Frontend Plugins
status: provisional
authors:
- '@Hyperkid123'
owners:
project-areas:
- core
creation-date: 2024-01-17
---
<!--
**Note:** When your BEP is complete, all these pre-existing comments should be removed
When editing BEPs, aim for tightly-scoped, single-topic PRs to keep discussions focused. If you disagree with what is already in a document, open a new PR with suggested changes.
-->
# BEP: <!-- Your short, descriptive title -->
<!-- Before merging the initial BEP PR, create a feature issue and update the below link. You can wait with this step until the BEP is ready to be merged. -->
[**Discussion Issue**](https://github.com/backstage/backstage/issues/NNNNN)
- [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
<!--
The summary of the BEP is a few paragraphs long and give a high-level overview of the features to be implemented. It should be possible to read *only* the summary and understand what the BEP is proposing to accomplish and what impact it has for users.
-->
The dynamic frontend plugins feature is a way of loading additional frontend plugins at runtime, without the requirement of rebuilding and restarting a running backstage instance.
This system should significantly improve frontend plugin management for backstage instances.
The dynamic plugins leverage the declarative UI system to define what a plugin is and how it should be represented in the browser.
## Motivation
<!--
This section is for explicitly listing the motivation, goals, and non-goals of
this BEP. Describe why the change is important and the benefits to users.
-->
### Goals
<!--
List the specific goals of the BEP. What is it trying to achieve? How will we
know that this has succeeded?
-->
- discover and choose tooling to enable dynamic frontend plugins
- easy way of converting existing frontend plugins to be dynamic
- turning on and off UI plugins at runtime without the requirement of rebuilding or restarting backstage
- manage dynamic plugins declaratively
An additional goal might be exploring the scope of a "on demand" rending and loading. Currently all plugins, or at least some portion of them must be loaded at UI bootstrap. This approach is not fully dynamic and might be considered a waste of resources. Fully optimizing dynamic plugins loading would require additional changes to the new UI system.
### Non-Goals
<!--
What is out of scope for this BEP? Listing non-goals helps to focus discussion
and make progress.
-->
## Proposal
<!--
This is where we get down to the specifics of what the proposal actually is.
This should have enough detail that reviewers can understand exactly what
you're proposing, but should not include things like API designs or
implementation.
-->
### Definition of UI dynamic plugin
A dynamic UI plugin (from now just plugin) is a plugin that is not part of the output of a backstage instance build. The plugin and its assets are injected into backstage at runtime. In this case, its injected into the browser at some point during user session.
From the user POV, there is no difference between classic and dynamic plugins.
The difference is known only to maintainers and should be limited to
- build requirements
- integration into backstage
### Dynamic loading tool
Traditional plugins are not dynamic out of the box. Additional changes are required during build time to make a plugin dynamic.
The main application (shell) also requires some changes to be able to load, inject, and propagate mandatory context to dynamic plugins.
Over the last few year, [Module Federation](https://github.com/module-federation) has become the standard when it comes to dynamically load JS modules browser (and nodejs) environment from remote locations and sharing context between the shell and the remote module.
Although there are other options, like externalizing dependencies, the module federation has proven itself as a robust solution to this particular problem.
### Module federation implementation
There are multiple available implementations of module federation.
Historically, module federation was implemented as a [Webpack feature](https://webpack.js.org/). Since then, additional implementations were created. Mainly for [`rspack`](https://www.rspack.dev/) and [Vite](https://github.com/originjs/vite-plugin-federation).
Recent changes _claim_, that all of these module federation implementations should be compatible with each other and there should be no need for locking backstage into a single implementation.
Compatibility between Webpack and `rspack` should available via the [@module-federation/enhanced](https://www.npmjs.com/package/@module-federation/enhanced) package. The compatibility is further [described here](https://www.rspack.dev/blog/module-federation-added-to-rspack.html#introducing-rspack-050).
The Vite plugin claims webpack compatibility as well. The Vite plugin _is not part of the @module-federation_ organization. The extend of compatibility is at this time unknown.
**The level of compatibility has to be tested.** Before then, no decision in regards to which implementation(s) will be used should be made. The chosen tool, or their mix, will shape the design and implementation.
That said, packages from the `@module-federation/*` organization should have higher priority as they are based directly on the module federation concepts and are well supported.
### Plugin integration into shell applications
Plugins should be integrated via the new UI system. The system already provides an asynchronous way of loading plugins. The dynamic plugins can be loaded in a similar fashion.
Plugins should be defined declaratively through configuration. Similar to what was described in this [RFC](https://github.com/backstage/backstage/issues/18372) and in this [issue](https://github.com/backstage/backstage/issues/19545).
### Plugin registry
Because plugins are not available at build time, some sort of registry needs to exist to store the information.
This registry needs to be mutable at runtime (add/remove new plugin metadata) and changes have to be reflected on session refresh.
This is currently an issue as the app config is embedded into JS assets during build time.
Each plugin is required to provide manifest file (metadata) in predefined format. This manifest will be used to inject the plugin assets into the browser.
### Plugin discovery
Plugin discovery is a pre-requisite for Plugin registry. This should be responsible for scanning for available plugins and generating/modifying the plugin registry to always keep it up to date.
## Design Details
> NOTE The details are based on the Janus implementation of dynamic frontend plugins. The implementation leverages [Scalprum](https://github.com/scalprum/scaffolding) which is a Webpack based dynamic plugin manager for React applications.
### Module federation implementation experiments
> NOTE Share outcome of testing mixing multiple tools for module federation.
Test should consist of trying to run permutations of webpack/Rspack/vite based shell apps/plugins and discover if we can freely choose any tool, or if we should restrict the tooling to just a subset of the available options.
### Plugin manifest
Each plugin should have a manifest file with important metadata. This metadata is used to load the remote assets to browser. The plugin manifest should be part of a build output.
A manifest should have:
- name of plugin
- how can be the init container accessed
- what is the base URL (assets pathname)
- name of the entry script(s)
#### Scalprum compatible manifest
```TS
type PluginManifest = {
name: string;
version: string;
dependencies?: Record<string, string>;
customProperties?: AnyObject;
baseURL: string;
extensions: Extension[];
loadScripts: string[];
registrationMethod: 'callback' | 'custom';
buildHash?: string;
}
```
```js
{
"name": "backstage.plugin-github-actions",
"version": "0.6.6",
"extensions": [],
"registrationMethod": "callback", // where container init is available in browser
"baseURL": "auto",
"loadScripts": [
"backstage.plugin-github-actions.804b91040fcbca6585ce.js"
],
"buildHash": "804b91040fcbca6585ce1bcd4b1f8aa2"
}
```
##### `registrationMethod`
Refers to [webpack output.libraryTarget](https://webpack.js.org/configuration/output/#outputlibrarytarget).
Callback refers to `jsonp` and a the `custom` is used if other available target configuration has been picked.
It is recommended to use either `global` or `jsonp` as these are environment agnostic (browser VS node). `jsonp` requires additional configurations. The `global` is preferable due to its simplicity.
##### `baseURL`
The `baseURL` is derived from [webpack public path](https://webpack.js.org/configuration/output/#outputpublicpath)
The public path can also be set to `auto` to remove the need to specify origin or pathname and resolve the pathname at runtime.
In Scalprum, some manifest [post processing](https://github.com/janus-idp/backstage-showcase/blob/main/packages/app/src/components/DynamicRoot/DynamicRoot.tsx#L323) is required to load the initial scripts if the `auto` baseURL is chosen.
### Plugin registry
Plugin registry can be fairly simplistic. It can be as simple as JSON file containing list/map of available plugins and their manifests
```TS
type RegistryEntry = {
name: string // plugin name
manifestLocation: string // path to the manifest resource
}
// object for easy access
type PluginRegistry = {
[pluginName: string]: RegistryEntry
}
// or as an array
type PluginRegistry = RegistryEntry[]
```
Example of such registry
```JS
// as object
{
"backstage.plugin-github-actions": {
"name": "backstage.plugin-github-actions",
"manifestLocation": "https://foo-bar.com/api/plugin-storage/plugin-manifest.json"
},
// ..rest of plugins
}
// as array
[
{
"name": "backstage.plugin-github-actions",
"manifestLocation": "https://foo-bar.com/api/plugin-storage/plugin-manifest.json"
},
// ...rest of plugins
]
```
Scalprum by default lazy loads plugins and manifests. That is because Scalprum initializes plugins only once they are supposed to be rendered in browser.
Because backstage does not require that functionality in initial dynamic plugin implementation and loads all plugins at bootstrap, there is an alternative to embed manifest data into the registry itself.
```TS
type RegistryEntry = {
name: string // plugin name
pluginManifest: PluginManifest
}
// object for easy access
type PluginRegistry = {
[pluginName: string]: RegistryEntry
}
// or as an array
type PluginRegistry = RegistryEntry[]
```
Example of registry with embedded manifests
```JS
// as object
{
"backstage.plugin-github-actions": {
"name": "backstage.plugin-github-actions",
"pluginManifest": {
"name": "backstage.plugin-github-actions",
"version": "0.6.6",
"extensions": [],
"registrationMethod": "callback",
"baseURL": "auto",
"loadScripts": [
"backstage.plugin-github-actions.804b91040fcbca6585ce.js"
],
"buildHash": "804b91040fcbca6585ce1bcd4b1f8aa2"
}
},
// ..rest of plugins
}
// as array
[
{
"name": "backstage.plugin-github-actions",
"pluginManifest": {
"name": "backstage.plugin-github-actions",
"version": "0.6.6",
"extensions": [],
"registrationMethod": "callback",
"baseURL": "auto",
"loadScripts": [
"backstage.plugin-github-actions.804b91040fcbca6585ce.js"
],
"buildHash": "804b91040fcbca6585ce1bcd4b1f8aa2"
}
},
// ...rest of plugins
]
```
### Webpack build configuration
> NOTE This is a sample current configuration in the Janus project. It uses Scalprum webpack based build plugin to generate the output. It does not take the [all options](#Module-federation-implementation-experiments) in consideration. This section will likely change considerably.
Part of a Scalprum tooling is also [dynamic-plugins-sdk](https://github.com/openshift/dynamic-plugin-sdk). Right now the package is a part of a different project, but that is about to change. More details in the [Scalprum roadmap](https://github.com/scalprum/scaffolding/blob/main/ROADMAP.md).
#### Sample plugin configuration using Scalprum SDK
```TS
import { DynamicRemotePlugin } from '@openshift/dynamic-plugin-sdk-webpack';
const sharedModules = {
/**
* Mandatory singleton packages for sharing
*/
react: {
singleton: true,
requiredVersion: '*',
},
'react-dom': {
singleton: true,
requiredVersion: '*',
},
'react-router-dom': {
singleton: true,
requiredVersion: '*',
},
'react-router': {
singleton: true,
requiredVersion: '*',
},
...
/**
* Full list of shared modules in Janus
* https://github.com/janus-idp/backstage-plugins/blob/87a6b045c7b0f301ebed8b8f99dc1741fa2b044b/packages/cli/src/lib/bundler/scalprumConfig.ts#L16
*/
}
const dynamicPluginPlugin = new DynamicRemotePlugin({
extensions: [],
sharedModules,
entryScriptFilename: `${options.pluginMetadata.name}.[contenthash].js`,
pluginMetadata: {
// version cna be used from the package.json version field
version: '1.0.0',
/**
* Name can be easily derived from the plugin name
* https://github.com/janus-idp/backstage-plugins/blob/87a6b045c7b0f301ebed8b8f99dc1741fa2b044b/packages/cli/src/commands/export-dynamic-plugin/frontend.ts#L40
*/
name: 'backstage.plugin-github-actions',
/**
* Path to the plugin entry point.
* It can default to the same entry point as in regular build.
* Plugins can expose multiple modules. We have found that one is sufficient from backstage plugins.
*/
exposedModules: {
PluginRoot: './src/index.ts'
}
},
});
```
The `DynamicRemotePlugin` webpack plugin takes care of the rest, including the manifest generation.
#### Sample plugin raw webpack configuration
The Scalprum config translates to a following base webpack plugin configuration:
```JS
import { container } from 'webpack';
const dynamicPlugin = new container.ModuleFederationPlugin({
name: 'backstage.plugin-github-actions',
library: {
type: 'global',
/**
* Some library.type has name limitation
* for example, if "type": "var" is used, the library.name can contain the "-" character
*/
name: 'backstage.plugin-github-actions'
},
filename: 'backstage.plugin-github-actions.[contenthash].js',
exposes: {
PluginRoot: './src/index.ts'
},
// list of shared modules like "react", "react-dom", etc
shared: sharedModules,
});
```
Additional plugin would have to be written from scratch to generate the plugin manifest file.
### Sample shell application webpack configuration
Shell application (backstage) has very simple module federation configuration. It does not require anything special to be able to inject plugins. Main requirement is to provide core shared packages like react and react-dom.
```JS
import { container } from 'webpack';
const scalprumPlugin = new container.ModuleFederationPlugin({
name: 'backstageHost',
filename: 'backstageHost.[contenthash].js',
// same share modules list as with plugin config
shared: [sharedModules],
});
```
Nothing else is required.
### Plugin storage
Where will the dynamic plugin assets be hosted? Module federation does not strictly require the remote assets to be all hosted on the same origin.
Theoretically plugins can be hosted on some "public CDN" which is detached from individual backstage instances.
Assets can be also be hosted in the same way as they have always been.
### Plugin discovery
How to notify/send data to browser
### Plugin initialization
Currently all plugins have to be initialized at UI bootstrap (page refresh). The new UI async API can be used to initialize the remote assets.
The `CreateAppFeatureLoader` can be leveraged to initialize the remote container.
```TS
const allPluginManifests = {...} // get this from the config
const asyncFeatureLoaders: CreateAppFeatureLoader[] = Object.values(allPluginManifests).map(({ manifest }) => {
return {
getLoaderName: () => manifest.name,
load: (options) => {
// initialize the remote container, depends on tooling
const plugin = initDynamicPlugin(manifest)
}
}
})
const app = createApp({
features: [
...asyncFeatureLoaders,
// rest of classic features
]
})
```
### Plugin declarative configuration
Janus dynamic plugins ref: https://github.com/janus-idp/backstage-showcase/blob/main/showcase-docs%2Fdynamic-plugins.md#frontend-layout-configuration
TBD, depends heavily on the new UI system
<!--
This section should contain enough information that the specifics of your
change are understandable. This may include API specs or even code snippets.
If there's any ambiguity about HOW your proposal will be implemented, this is the place to discuss them.
-->
## Release Plan
<!--
This section should describe the rollout process for any new features. It must take our version policies into account and plan for a phased rollout if this change affects any existing stable APIs.
If there is any particular feedback to be gathered during the rollout, this should be described here as well.
-->
## Dependencies
<!--
List any dependencies that this work has on other BEPs or features.
-->
## Alternatives
<!--
What other approaches did you consider, and why did you rule them out? These do
not need to be as detailed as the proposal, but should include enough
information to express the idea and why it was not acceptable.
-->
+99
View File
@@ -0,0 +1,99 @@
---
title: BEP Title
status: provisional
authors:
- '@ghost'
owners:
project-areas:
- aaa
- bbb
creation-date: yyyy-mm-dd
---
<!--
**Note:** When your BEP is complete, all these pre-existing comments should be removed
When editing BEPs, aim for tightly-scoped, single-topic PRs to keep discussions focused. If you disagree with what is already in a document, open a new PR with suggested changes.
-->
# BEP: <!-- Your short, descriptive title -->
<!-- Before merging the initial BEP PR, create a feature issue and update the below link. You can wait with this step until the BEP is ready to be merged. -->
[**Discussion Issue**](https://github.com/backstage/backstage/issues/NNNNN)
- [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
<!--
The summary of the BEP is a few paragraphs long and give a high-level overview of the features to be implemented. It should be possible to read *only* the summary and understand what the BEP is proposing to accomplish and what impact it has for users.
-->
## Motivation
<!--
This section is for explicitly listing the motivation, goals, and non-goals of
this BEP. Describe why the change is important and the benefits to users.
-->
### Goals
<!--
List the specific goals of the BEP. What is it trying to achieve? How will we
know that this has succeeded?
-->
### Non-Goals
<!--
What is out of scope for this BEP? Listing non-goals helps to focus discussion
and make progress.
-->
## Proposal
<!--
This is where we get down to the specifics of what the proposal actually is.
This should have enough detail that reviewers can understand exactly what
you're proposing, but should not include things like API designs or
implementation.
-->
## Design Details
<!--
This section should contain enough information that the specifics of your
change are understandable. This may include API specs or even code snippets.
If there's any ambiguity about HOW your proposal will be implemented, this is the place to discuss them.
-->
## Release Plan
<!--
This section should describe the rollout process for any new features. It must take our version policies into account and plan for a phased rollout if this change affects any existing stable APIs.
If there is any particular feedback to be gathered during the rollout, this should be described here as well.
-->
## Dependencies
<!--
List any dependencies that this work has on other BEPs or features.
-->
## Alternatives
<!--
What other approaches did you consider, and why did you rule them out? These do
not need to be as detailed as the proposal, but should include enough
information to express the idea and why it was not acceptable.
-->
+86
View File
@@ -0,0 +1,86 @@
# Backstage Enhancement Proposals (BEPs)
A Backstage Enhancement Proposal (BEP) is a way to propose, communicate and coordinate on new efforts for the Backstage project.
## Quick start for the BEP process
1. Discuss the idea with the community and maintainers. Either here on GitHub, Discord, or during community sessions or SIG meetings.
Make sure that others think the work is worth taking up and will help review the BEP and any code changes required.
1. Make a copy of the [BEP template](./NNNN-template/) directory as `beps/NNNN-short-descriptive-title`, where `NNNN` is the next available number padded with leading zeroes.
1. Fill out as much of the YAML metadata as you can.
1. Fill out the template as best you can.
1. If you want the BEP to be owned by a particular project area, add an entry for the BEP folder to [CODEOWNERS](../.github/CODEOWNERS).
1. Create a PR for the BEP. Title it "BEP: &lt;title&gt;". Aim to get the high level goals clarified and avoid getting hung up on specific details. The PR can be merged early and iterated on.
1. Once the BEP is ready to be merged, create a [feature issue](https://github.com/backstage/backstage/issues/new?template=feature.yaml) titled "BEP: &lt;title&gt;". Use the summary of the BEP as description and link to the BEP PR in the context section. Once the PR is merged, the issue should be updated to link to the BEP folder instead. This issue will serve as a general discussion issue for the BEP.
1. Once the initial BEP is merged you should keep iterating on it until it is ready to leave that `provisional` state. Leaving the `provisional` state is a decision made by the project area maintainers.
Just because a BEP is merged does not mean it is complete or approved for implementation. Any BEP marked as `provisional` is a working document and subject to change.
The authors of the BEP are also responsible for driving the BEP forward all the way to implementation. The approval of a BEP is not a commitment to implement it.
## FAQs
### Do I have to use the BEP process?
No, but it is recommended for larger changes and contributions that affect the Backstage framework or core features where you want to make design decisions upfront and iterate on them across several PRs.
### What is the difference between a BEP and an RFC issue?
An RFC issue is a good way to receive feedback on an idea. The BEP process may involve getting feedback on an idea, but it takes things further by making it easier to iterate on the design of a new feature, getting it approved by the owning project area maintainers, and tracking its implementation.
While anyone can open an RFC issue, the features added by a BEP need to be approved by the owners before it can be merged. The BEP will also contain the latest agreed upon design, whereas an RFC issue is more of a discussion forum.
Often a BEP can start out as an RFC issue, which may then be converted into a BEP once the idea has received an owner and been approved.
### Should I use the BEP process for new or existing Backstage plugins?
No, except for plugins that implement core features of Backstage. The BEP process is only intended for changes to the Backstage framework and core features.
### Can I update an existing BEP?
Yes! As long as the BEP is still in the `provisional` state you should keep iterating on it. Please keep each PR focused on a single topic and avoid long-running and overly broad PRs.
### Can I update a BEP that was submitted by someone else?
Yes! BEPs are living documents and anyone can suggest changes to them. We encourage to challenge the design decisions in a BEP and suggest alternatives. You can also help fill out details in a BEP that has not yet been fully fleshed out.
### What's the difference between a BEP and an [ADR](https://backstage.io/docs/architecture-decisions/)?
Architecture Decision Records (ADRs) are used to document decisions made for development within the Backstage project. They are not intended to be used for proposing new features or changes to Backstage.
### My FAQ isn't answered here!
The BEP process is still evolving!
If something is missing or not answered here feel free to reach out on our community [Discord](https://discord.gg/backstage-687207715902193673).
If you want to propose a change to the BEP process you can also open a PR directly with your proposal.
### BEP Metadata
At the start of each BEP is a YAML document that contains metadata about the BEP. These are the fields that should currently be provided:
| Field | Description |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **title** | The title of the BEP in plain language. The title will also be used in the BEP filename. See the template for instructions and details. |
| **status** | The current state of the BEP, must be one of the values listed in the [BEP Status](#bep-status) section. |
| **authors** | A list of GitHub IDs for the authors of the BEP. Typically only the original authors are included, but additional authors can be added if significant contributions are made. |
| **owners** | A list of GitHub IDs for the owners of the BEP. The owners are responsible for the implementation of the BEP, and are required for the BEP to move to the `implementable` status. |
| **project-areas** | The Project Areas closely associated with this BEP that need to approve it. For a list of existing project areas, see [OWNERS.md](../OWNERS.md). If the BEP is not related to any existing project area, use `core-maintainers` instead. |
| **creation-date** | The `yyyy-mm-dd` date that the BEP was first submitted in a PR. |
## BEP Status
The BEP Status is critical to clearly communicate the status of each BEP. Each BEP must have its status field set to one of the following values:
- `provisional`: The BEP has been proposed and is actively being defined. The BEP as been approved by the owning project area maintainers as work to be done.
- `implementable`: The approvers have approved this BEP for implementation.
- `implemented`: The BEP has been implemented and is no longer actively changed.
- `deferred`: The BEP is proposed but not actively being worked on.
- `rejected`: The approvers and authors have decided that this BEP is not moving forward.
The BEP is kept around as a historical document.
- `withdrawn`: The authors have withdrawn the BEP.
- `replaced`: The BEP has been replaced by a new BEP.
### Prior Art
The BEP process is heavily inspired by the [Kubernetes Enhancement Proposal (KEP) process](https://github.com/kubernetes/enhancements/blob/master/keps/README.md).
-6
View File
@@ -1,6 +0,0 @@
dependencies:
- name: postgresql
repository: https://charts.bitnami.com/bitnami
version: 13.2.27
digest: sha256:c2272865ffe4c805153440d8fc6af84945d4ce566b14d0fa8698d75382ada06c
generated: "2023-12-31T14:23:23.75461574Z"
-30
View File
@@ -1,30 +0,0 @@
apiVersion: v2
name: backstage
description: A Helm chart for Backstage
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
version: 0.1.3
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application.
appVersion: v0.1.1-alpha.23
sources:
- https://github.com/backstage/backstage
- https://github.com/spotify/lighthouse-audit-service
dependencies:
- name: postgresql
condition: postgresql.enabled
version: 13.2.27
repository: https://charts.bitnami.com/bitnami
maintainers:
- name: Martina Iglesias Fernández
email: martina@roadie.io
url: https://roadie.io
- name: David Tuite
email: david@roadie.io
url: https://roadie.io
-282
View File
@@ -1,282 +0,0 @@
# Backstage demo helm charts
This folder contains Helm charts that can easily create a Kubernetes deployment of a demo Backstage app.
### Pre-requisites
These charts depend on the `nginx-ingress` controller being present in the cluster. If it's not already installed you
can run:
```shell
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install nginx-ingress ingress-nginx/ingress-nginx
```
### Installing the charts
After choosing a DNS name where backstage will be hosted create a yaml file for your custom configuration.
```yaml
appConfig:
app:
baseUrl: https://backstage.mydomain.com
title: Backstage
backend:
baseUrl: https://backstage.mydomain.com
cors:
origin: https://backstage.mydomain.com
lighthouse:
baseUrl: https://backstage.mydomain.com/lighthouse-api
techdocs:
storageUrl: https://backstage.mydomain.com/api/techdocs/static/docs
requestUrl: https://backstage.mydomain.com/api/techdocs
```
Then use it to run:
```shell
git clone https://github.com/backstage/backstage.git
cd contrib/chart/backstage
helm dependency update
helm install -f backstage-mydomain.yaml backstage .
```
This command will deploy the following pieces:
- Backstage frontend
- Backstage backend with scaffolder and auth plugins
- (optional) a PostgreSQL instance
- lighthouse plugin
- ingress
After a few minutes Backstage should be up and running in your cluster under the DNS specified earlier.
Make sure to create the appropriate DNS entry in your infrastructure. To find the public IP address run:
```shell
$ kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
backstage-ingress * 123.1.2.3 80 17m
```
> **NOTE**: this is not a production ready deployment.
## Customization
### Issue certificates
These charts can install or reuse a `clusterIssuer` to generate certificates for the backstage `ingress`. To do that:
1. [Install][install-cert-manager] or make sure [cert-manager][cert-manager] is installed in the cluster.
2. Enable the issuer in the charts. This will first check if there is a `letsencrypt` issuer already deployed in your
cluster and deploy one if it doesn't exist.
To enable it you need to provide a valid email address in the chart's values:
```yaml
issuer:
email: me@example.com
clusterIssuer: 'letsencrypt-prod'
```
By default, the charts use `letsencrypt-staging` so in the above example we instruct helm to use the production issuer
instead.
[cert-manager]: https://cert-manager.io/docs/
[install-cert-manager]: https://cert-manager.io/docs/installation/kubernetes/#installing-with-helm
### Custom PostgreSQL instance
Configuring a connection to an existing PostgreSQL instance is possible through the chart's values.
First create a yaml file with the configuration you want to override, for example `backstage-prod.yaml`:
```yaml
postgresql:
enabled: false
appConfig:
app:
baseUrl: https://backstage-demo.mydomain.com
title: Backstage
backend:
baseUrl: https://backstage-demo.mydomain.com
cors:
origin: https://backstage-demo.mydomain.com
database:
client: pg
connection:
database: backstage_plugin_catalog
host: <host>
user: <pg user>
password: <password>
lighthouse:
baseUrl: https://backstage-demo.mydomain.com/lighthouse-api
lighthouse:
database:
client: pg
connection:
host: <host>
user: <pg user>
password: <password>
database: lighthouse_audit_service
```
For the CA, create a `configMap` named `<release name>-<chart name>-postgres-ca` with a file called `ca.crt`:
```shell
kubectl create configmap my-company-backstage-postgres-ca --from-file=ca.crt"
```
or disable CA mount
```yaml
backend:
postgresCertMountEnabled: false
lighthouse:
postgresCertMountEnabled: false
```
> Where the release name contains the chart name "backstage" then only the release name will be used.
Now install the helm chart:
```shell
cd contrib/chart/backstage
helm install -f backstage-prod.yaml my-backstage .
```
### Use your own docker images
The docker images used for the deployment can be configured through the charts values:
```yaml
frontend:
image:
repository: <image-name>
tag: <image-tag>
backend:
image:
repository: <image-name>
tag: <image-tag>
lighthouse:
image:
repository: <image-name>
tag: <image-tag>
```
### Use a private docker repo
Create a docker-registry secret
```shell
kubectl create secret docker-registry <docker_registry_secret_name> # args
```
> For private images on docker hub --docker-server can be set to docker.io
Reference the secret in your chart values
```yaml
dockerRegistrySecretName: <docker_registry_secret_name>
```
### Different namespace
To install the charts a specific namespace use `--namespace <ns>`:
```shell
helm install -f my_values.yaml --namespace demos backstage .
```
### Disable loading of demo data
To deploy backstage with the pre-loaded demo data disable `backend.demoData`:
```shell
helm install -f my_values.yaml --set backend.demoData=false backstage .
```
### Other options
For more customization options take a look at the [values.yaml](/contrib/chart/backstage/values.yaml) file.
## Troubleshooting
Some resources created by these charts are meant to survive after upgrades and even after uninstalls. When
troubleshooting these charts it can be useful to delete these resources between re-installs.
Secrets:
```
<release-name>-postgresql-certs -- contains the certificates used by the deployed PostgreSQL
```
Persistent volumes:
```
data-<release-name>-postgresql-0 -- this is the data volume used by PostgreSQL to store data and configuration
```
> **NOTE**: this volume also stores the configuration for PostgreSQL which includes things like the password for the
> `postgres` user. This means that uninstalling and re-installing the charts with `postgres.enabled` set to `true` and
> auto generated passwords will fail. The solution is to delete this volume with
> `kubectl delete pvc data-<release-name>-postgresql-0`
ConfigMaps:
```
<release-name>-postgres-ca -- contains the generated CA certificate for PostgreSQL when `postgres` is enabled
```
#### Unable to verify signature
```
Backend failed to start up Error: unable to verify the first certificate
at TLSSocket.onConnectSecure (_tls_wrap.js:1501:34)
at TLSSocket.emit (events.js:315:20)
at TLSSocket._finishInit (_tls_wrap.js:936:8)
at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:710:12) {
code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE'
```
This error happens in the backend when it tries to connect to the configured PostgreSQL database and the specified CA is not correct. The solution is to make sure that the contents of the `configMap` that holds the certificate match the CA for the PostgreSQL instance. A workaround is to set `appConfig.backend.database.connection.ssl.rejectUnauthorized` to `false` in the chart's values.
#### Multi-Platform Kubernetes Services
If you are running a multi-platform Kubernetes service with Windows and Linux nodes then you will need to apply a `nodeSelector` to the Helm chart to ensure that pods are scheduled onto the correct platform nodes.
Add the following to your Helm values file:
```yaml
global:
nodeSelector:
kubernetes.io/os: linux
# If using Postgres Chart also add
postgresql:
master:
nodeSelector:
kubernetes.io/os: linux
slave:
nodeSelector:
kubernetes.io/os: linux
```
<!-- TODO Add example command when we know the final name of the charts -->
## Uninstalling Backstage
To uninstall Backstage simply run:
```shell
RELEASE_NAME=<release-name> # use `helm list` to find out the name
helm uninstall ${RELEASE_NAME}
kubectl delete pvc data-${RELEASE_NAME}-postgresql-0
kubectl delete secret ${RELEASE_NAME}-postgresql-certs
kubectl delete configMap ${RELEASE_NAME}-postgres-ca
```
@@ -1,51 +0,0 @@
backend:
lighthouseHostname: {{ include "lighthouse.serviceName" . | quote }}
listen:
port: {{ .Values.appConfig.backend.listen.port | default 7007 }}
database:
client: {{ .Values.appConfig.backend.database.client | quote }}
connection:
host: {{ include "backend.postgresql.host" . | quote }}
port: {{ include "backend.postgresql.port" . | quote }}
user: {{ include "backend.postgresql.user" . | quote }}
database: {{ .Values.appConfig.backend.database.connection.database | quote }}
ssl:
rejectUnauthorized: {{ .Values.appConfig.backend.database.connection.ssl.rejectUnauthorized | quote }}
ca: {{ include "backstage.backend.postgresCaFilename" . | quote }}
catalog:
{{- if .Values.backend.demoData }}
locations:
# Backstage example components
- type: github
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-components.yaml
# Example component for github-actions
- type: github
target: https://github.com/backstage/backstage/blob/master/plugins/github-actions/examples/sample.yaml
# Example component for techdocs
- type: github
target: https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component/documented-component.yaml
# Backstage example APIs
- type: github
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-apis.yaml
# Backstage example templates
- type: github
target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/all-templates.yaml
{{- else }}
locations: []
{{- end }}
auth:
providers:
microsoft: null
scaffolder:
azure: null
sentry:
organization: {{ .Values.appConfig.sentry.organization | quote }}
techdocs:
generator:
runIn: 'local'
@@ -1,13 +0,0 @@
{{ $backendDb := .Values.appConfig.backend.database.connection.database }}
{{ $lighthouseDb := .Values.lighthouse.database.connection.database }}
{{ $user := .Values.global.postgresql.postgresqlUsername }}
grant all privileges on database {{ $backendDb }} to {{ $user }};
create database backstage_plugin_auth;
grant all privileges on database backstage_plugin_auth to {{ $user }};
{{ if not (eq $backendDb $lighthouseDb) }}
create database {{ $lighthouseDb }};
grant all privileges on database {{ $lighthouseDb }} to {{ $user }};
{{ end }}
@@ -1,286 +0,0 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "backstage.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "backstage.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "backstage.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Common App labels
*/}}
{{- define "backstage.app.labels" -}}
app.kubernetes.io/name: {{ include "backstage.name" . }}-app
helm.sh/chart: {{ include "backstage.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{/*
Common Backend labels
*/}}
{{- define "backstage.backend.labels" -}}
app.kubernetes.io/name: {{ include "backstage.name" . }}-backend
helm.sh/chart: {{ include "backstage.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{/*
Name for postgresql dependency
See https://github.com/helm/helm/issues/3920#issuecomment-686913512
*/}}
{{- define "backstage.postgresql.fullname" -}}
{{ printf "%s-%s" .Release.Name .Values.postgresql.nameOverride }}
{{- end -}}
{{/*
Create the name of the service account to use for the app
*/}}
{{- define "backstage.app.serviceAccountName" -}}
{{- if .Values.app.serviceAccount.create -}}
{{ default "default" .Values.app.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.app.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{/*
Create the name of the service account to use for the backend
*/}}
{{- define "backstage.backend.serviceAccountName" -}}
{{- if .Values.backend.serviceAccount.create -}}
{{ default default .Values.backend.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.backend.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{/*
Path to the CA certificate file in the backend
*/}}
{{- define "backstage.backend.postgresCaFilename" -}}
{{ include "backstage.backend.postgresCaDir" . }}/{{- required "The name for the CA certificate file for postgresql is required" .Values.global.postgresql.caFilename }}
{{- end -}}
{{/*
{{/*
Directory path to the CA certificate file in the backend
*/}}
{{- define "backstage.backend.postgresCaDir" -}}
{{- if .Values.appConfig.backend.database.connection.ssl.ca -}}
{{ .Values.appConfig.backend.database.connection.ssl.ca }}
{{- else -}}
/etc/postgresql
{{- end -}}
{{- end -}}
{{/*
Path to the CA certificate file in lighthouse
*/}}
{{- define "backstage.lighthouse.postgresCaFilename" -}}
{{ include "backstage.lighthouse.postgresCaDir" . }}/{{- required "The name for the CA certificate file for postgresql is required" .Values.global.postgresql.caFilename }}
{{- end -}}
{{/*
Directory path to the CA certificate file in lighthouse
*/}}
{{- define "backstage.lighthouse.postgresCaDir" -}}
{{- if .Values.lighthouse.database.pathToDatabaseCa -}}
{{ .Values.lighthouse.database.pathToDatabaseCa }}
{{- else -}}
/etc/postgresql
{{- end -}}
{{- end -}}
{{/*
{{/*
Generate ca for postgresql
*/}}
{{- define "backstage.postgresql.generateCA" -}}
{{- $ca := .ca | default (genCA (include "backstage.postgresql.fullname" .) 365) -}}
{{- $_ := set . "ca" $ca -}}
{{- $ca.Cert -}}
{{- end -}}
{{/*
Generate certificates for postgresql
*/}}
{{- define "generateCerts" -}}
{{- $postgresName := (include "backstage.postgresql.fullname" .) }}
{{- $altNames := list $postgresName ( printf "%s.%s" $postgresName .Release.Namespace ) ( printf "%s.%s.svc" ( $postgresName ) .Release.Namespace ) -}}
{{- $ca := .ca | default (genCA (include "backstage.postgresql.fullname" .) 365) -}}
{{- $_ := set . "ca" $ca -}}
{{- $cert := genSignedCert ( $postgresName ) nil $altNames 365 $ca -}}
tls.crt: {{ $cert.Cert | b64enc }}
tls.key: {{ $cert.Key | b64enc }}
{{- end -}}
{{/*
Generate a password for the postgres user used for the connections from the backend and lighthouse
*/}}
{{- define "postgresql.generateUserPassword" -}}
{{- $pgPassword := .pgPassword | default ( randAlphaNum 12 ) -}}
{{- $_ := set . "pgPassword" $pgPassword -}}
{{ $pgPassword}}
{{- end -}}
{{/*
Name of the backend service
*/}}
{{- define "backend.serviceName" -}}
{{ include "backstage.fullname" . }}-backend
{{- end -}}
{{/*
Name of the frontend service
*/}}
{{- define "frontend.serviceName" -}}
{{ include "backstage.fullname" . }}-frontend
{{- end -}}
{{/*
Name of the lighthouse backend service
*/}}
{{- define "lighthouse.serviceName" -}}
{{ include "backstage.fullname" . }}-lighthouse
{{- end -}}
{{/*
Name of the postgresql service
*/}}
{{- define "postgresql.serviceName" -}}
{{- include "backstage.postgresql.fullname" . }}
{{- end -}}
{{/*
Postgres host for lighthouse
*/}}
{{- define "lighthouse.postgresql.host" -}}
{{- if .Values.postgresql.enabled }}
{{- include "postgresql.serviceName" . }}
{{- else -}}
{{- required "A valid .Values.lighthouse.database.connection.host is required when postgresql is not enabled" .Values.lighthouse.database.connection.host -}}
{{- end -}}
{{- end -}}
{{/*
Postgres host for the backend
*/}}
{{- define "backend.postgresql.host" -}}
{{- if .Values.postgresql.enabled }}
{{- include "postgresql.serviceName" . }}
{{- else -}}
{{- required "A valid .Values.appConfig.backend.database.connection.host is required when postgresql is not enabled" .Values.appConfig.backend.database.connection.host -}}
{{- end -}}
{{- end -}}
{{/*
Postgres port for the backend
*/}}
{{- define "backend.postgresql.port" -}}
{{- if .Values.postgresql.enabled }}
{{- .Values.postgresql.service.port }}
{{- else if .Values.appConfig.backend.database.connection.port -}}
{{- .Values.appConfig.backend.database.connection.port }}
{{- else -}}
5432
{{- end -}}
{{- end -}}
{{/*
Postgres port for lighthouse
*/}}
{{- define "lighthouse.postgresql.port" -}}
{{- if .Values.postgresql.enabled }}
{{- .Values.postgresql.service.port }}
{{- else if .Values.lighthouse.database.connection.port -}}
{{- .Values.lighthouse.database.connection.port }}
{{- else -}}
5432
{{- end -}}
{{- end -}}
{{/*
Postgres user for backend
*/}}
{{- define "backend.postgresql.user" -}}
{{- if .Values.postgresql.enabled }}
{{- .Values.global.postgresql.postgresqlUsername }}
{{- else -}}
{{- required "A valid .Values.appConfig.backend.database.connection.user is required when postgresql is not enabled" .Values.appConfig.backend.database.connection.user -}}
{{- end -}}
{{- end -}}
{{/*
Postgres user for lighthouse
*/}}
{{- define "lighthouse.postgresql.user" -}}
{{- if .Values.postgresql.enabled }}
{{- .Values.global.postgresql.postgresqlUsername }}
{{- else -}}
{{- required "A valid .Values.lighthouse.database.connection.user is required when postgresql is not enabled" .Values.lighthouse.database.connection.user -}}
{{- end -}}
{{- end -}}
{{/*
Postgres password secret for backend
*/}}
{{- define "backend.postgresql.passwordSecret" -}}
{{- if .Values.postgresql.enabled }}
{{- template "backstage.postgresql.fullname" . }}
{{- else -}}
{{ $secretName := (printf "%s-backend-postgres" (include "backstage.fullname" . )) }}
{{- required "A valid .Values.appConfig.backend.database.connection.password is required when postgresql is not enabled" $secretName -}}
{{- end -}}
{{- end -}}
{{/*
Postgres password for lighthouse
*/}}
{{- define "lighthouse.postgresql.passwordSecret" -}}
{{- if .Values.postgresql.enabled }}
{{- template "backstage.postgresql.fullname" . }}
{{- else -}}
{{ $secretName := (printf "%s-lighthouse-postgres" (include "backstage.fullname" . )) }}
{{- required "A valid .Values.lighthouse.database.connection.password is required when postgresql is not enabled" $secretName -}}
{{- end -}}
{{- end -}}
{{/*
app-config file name
*/}}
{{- define "backstage.appConfigFilename" -}}
{{- "app-config.development.yaml" -}}
{{- end -}}
@@ -1,98 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "backstage.fullname" . }}-backend
spec:
replicas: {{ .Values.backend.replicaCount }}
selector:
matchLabels:
app: backstage
component: backend
template:
metadata:
annotations:
ad.datadoghq.com/backstage.logs: '[{"source":"backstage","service":"backend"}]'
labels:
app: backstage
component: backend
spec:
{{- if .Values.dockerRegistrySecretName }}
imagePullSecrets:
- name: {{ .Values.dockerRegistrySecretName }}
{{- end}}
containers:
- name: {{ .Chart.Name }}-backend
command: ["node"]
args:
- "packages/backend"
- "--config"
- "app-config.yaml"
- "--config"
- {{ printf "/usr/src/app/%s" (include "backstage.appConfigFilename" .) | quote }}
image: {{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag }}
imagePullPolicy: {{ .Values.backend.image.pullPolicy }}
ports:
- containerPort: {{ .Values.backend.containerPort }}
resources:
{{- toYaml .Values.backend.resources | nindent 12 }}
envFrom:
- secretRef:
name: {{ include "backstage.fullname" . }}-backend
- configMapRef:
name: {{ include "backstage.fullname" . }}-app-env
- configMapRef:
name: {{ include "backstage.fullname" . }}-auth
env:
- name: NODE_ENV
value: {{ .Values.backend.nodeEnv | default "development" }}
- name: APP_CONFIG_backend_database_connection_password
valueFrom:
secretKeyRef:
name: {{ include "backend.postgresql.passwordSecret" .}}
key: postgresql-password
volumeMounts:
{{- if .Values.backend.postgresCertMountEnabled }}
- name: postgres-ca
mountPath: {{ include "backstage.backend.postgresCaDir" . }}
{{- end }}
- name: app-config
mountPath: {{ printf "/usr/src/app/%s" (include "backstage.appConfigFilename" .) }}
subPath: {{ include "backstage.appConfigFilename" . }}
volumes:
{{- if .Values.backend.postgresCertMountEnabled }}
- name: postgres-ca
configMap:
name: {{ include "backstage.fullname" . }}-postgres-ca
{{- end }}
- name: app-config
configMap:
name: {{ include "backstage.fullname" . }}-app-config
{{- if .Values.global.nodeSelector }}
nodeSelector: {{- toYaml .Values.global.nodeSelector | nindent 8 }}
{{- end }}
{{- if .Values.backend.enabled }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "backend.serviceName" . }}
spec:
ports:
- port: 80
targetPort: {{ .Values.backend.containerPort }}
selector:
app: backstage
component: backend
type: {{ .Values.backend.serviceType }}
{{- end }}
@@ -1,24 +0,0 @@
{{- if .Values.backend.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "backstage.fullname" . }}-backend
type: Opaque
stringData:
AUTH_GOOGLE_CLIENT_SECRET: {{ .Values.auth.google.clientSecret }}
AUTH_GITHUB_CLIENT_SECRET: {{ .Values.auth.github.clientSecret }}
AUTH_GITLAB_CLIENT_SECRET: {{ .Values.auth.gitlab.clientSecret }}
AUTH_OKTA_CLIENT_SECRET: {{ .Values.auth.okta.clientSecret }}
AUTH_OAUTH2_CLIENT_SECRET: {{ .Values.auth.oauth2.clientSecret }}
AUTH_AUTH0_CLIENT_SECRET: {{ .Values.auth.auth0.clientSecret }}
AUTH_MICROSOFT_CLIENT_SECRET: {{ .Values.auth.microsoft.clientSecret }}
SENTRY_TOKEN: {{ .Values.auth.sentryToken }}
ROLLBAR_ACCOUNT_TOKEN: {{ .Values.auth.rollbarAccountToken }}
CIRCLECI_AUTH_TOKEN: {{ .Values.auth.circleciAuthToken }}
GITHUB_TOKEN: {{ .Values.auth.githubToken }}
GITLAB_TOKEN: {{ .Values.auth.gitlabToken }}
AZURE_TOKEN: {{ .Values.auth.azure.api.token }}
NEW_RELIC_REST_API_KEY: {{ .Values.auth.newRelicRestApiKey }}
TRAVISCI_AUTH_TOKEN: {{ .Values.auth.travisciAuthToken }}
PAGERDUTY_TOKEN: {{ .Values.auth.pagerdutyToken }}
{{- end }}
@@ -1,27 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "backstage.fullname" . }}-app-config
data:
{{ include "backstage.appConfigFilename" . | indent 2 }}: |
{{ tpl (.Files.Get "files/app-config.development.yaml.tpl") . | indent 4 }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "backstage.fullname" . }}-app-env
data:
APP_CONFIG_app_baseUrl: {{ .Values.appConfig.app.baseUrl | quote | quote }}
APP_CONFIG_app_title: {{ .Values.appConfig.app.title | quote | quote }}
APP_CONFIG_backend_baseUrl: {{ .Values.appConfig.backend.baseUrl | quote | quote }}
APP_CONFIG_backend_cors_origin: {{ .Values.appConfig.backend.cors.origin | quote | quote }}
APP_CONFIG_techdocs_storageUrl: {{ .Values.appConfig.techdocs.storageUrl | quote | quote }}
APP_CONFIG_techdocs_requestUrl: {{ .Values.appConfig.techdocs.requestUrl | quote | quote }}
APP_CONFIG_lighthouse_baseUrl: {{ .Values.appConfig.lighthouse.baseUrl | quote | quote }}
APP_CONFIG_backend_database_connection_ssl_rejectUnauthorized: "false"
APP_CONFIG_auth_providers_github_development_appOrigin: {{ .Values.appConfig.auth.providers.github.development.appOrigin | quote | quote }}
APP_CONFIG_auth_providers_google_development_appOrigin: {{ .Values.appConfig.auth.providers.google.development.appOrigin | quote | quote }}
APP_CONFIG_auth_providers_gitlab_development_appOrigin: {{ .Values.appConfig.auth.providers.gitlab.development.appOrigin | quote | quote }}
APP_CONFIG_auth_providers_okta_development_appOrigin: {{ .Values.appConfig.auth.providers.okta.development.appOrigin | quote | quote }}
APP_CONFIG_auth_providers_oauth2_development_appOrigin: {{ .Values.appConfig.auth.providers.oauth2.development.appOrigin | quote | quote }}
@@ -1,21 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "backstage.fullname" . }}-auth
data:
AUTH_GOOGLE_CLIENT_ID: {{ .Values.auth.google.clientId }}
AUTH_GITHUB_CLIENT_ID: {{ .Values.auth.github.clientId }}
AUTH_GITLAB_CLIENT_ID: {{ .Values.auth.gitlab.clientId }}
# This should not be prefixed with AUTH_. This could be a typo in the Backstage app config.
# Regardless, it is not decided by me.
GITLAB_BASE_URL: {{ .Values.auth.gitlab.baseUrl }}
AUTH_OKTA_CLIENT_ID: {{ .Values.auth.okta.clientId }}
AUTH_OKTA_AUDIENCE: {{ .Values.auth.okta.audience }}
AUTH_OAUTH2_CLIENT_ID: {{ .Values.auth.oauth2.clientId }}
AUTH_OAUTH2_AUTH_URL: {{ .Values.auth.oauth2.authUrl }}
AUTH_OAUTH2_TOKEN_URL: {{ .Values.auth.oauth2.tokenUrl }}
AUTH_AUTH0_CLIENT_ID: {{ .Values.auth.auth0.clientId }}
AUTH_AUTH0_DOMAIN: {{ .Values.auth.auth0.domain }}
AUTH_MICROSOFT_CLIENT_ID: {{ .Values.auth.microsoft.clientId }}
AUTH_MICROSOFT_TENANT_ID: {{ .Values.auth.microsoft.tenantId }}
@@ -1,66 +0,0 @@
{{- if .Values.frontend.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "backstage.fullname" . }}-frontend
spec:
replicas: {{ .Values.frontend.replicaCount }}
selector:
matchLabels:
app: backstage
component: frontend
template:
metadata:
annotations:
ad.datadoghq.com/backstage.logs: '[{"source":"backstage","service":"frontend"}]'
labels:
app: backstage
component: frontend
spec:
{{- if .Values.dockerRegistrySecretName }}
imagePullSecrets:
- name: {{ .Values.dockerRegistrySecretName }}
{{- end}}
containers:
- name: {{ .Chart.Name }}-frontend
image: {{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag }}
imagePullPolicy: {{ .Values.frontend.image.pullPolicy }}
ports:
- containerPort: {{ .Values.frontend.containerPort }}
resources:
{{- toYaml .Values.frontend.resources | nindent 12 }}
envFrom:
- configMapRef:
name: {{ include "backstage.fullname" . }}-app-env
volumeMounts:
- name: app-config
mountPath: {{ printf "/usr/share/nginx/html/static/%s" (include "backstage.appConfigFilename" .) }}
subPath: {{ include "backstage.appConfigFilename" . }}
volumes:
- name: app-config
configMap:
name: {{ include "backstage.fullname" . }}-app-config
{{- if .Values.global.nodeSelector }}
nodeSelector: {{- toYaml .Values.global.nodeSelector | nindent 8 }}
{{- end }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "frontend.serviceName" . }}
spec:
ports:
- port: 80
targetPort: {{ .Values.frontend.containerPort }}
selector:
app: backstage
component: frontend
type: {{ .Values.frontend.serviceType }}
{{- end }}
@@ -1,150 +0,0 @@
{{- $frontendUrl := urlParse .Values.appConfig.app.baseUrl}}
{{- $backendUrl := urlParse .Values.appConfig.backend.baseUrl}}
{{- $lighthouseUrl := urlParse .Values.appConfig.lighthouse.baseUrl}}
{{/* Determine the api type for the ingress */}}
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
apiVersion: networking.k8s.io/v1
{{- else -}}
apiVersion: networking.k8s.io/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ include "backstage.fullname" . }}-ingress
annotations:
{{- if .Values.issuer.email }}
cert-manager.io/cluster-issuer: {{ .Values.issuer.clusterIssuer }}
{{- end }}
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/configuration-snippet: |
if ($scheme = https) {
add_header Strict-Transport-Security "max-age=0;";
}
{{- toYaml .Values.ingress.annotations | nindent 4 }}
spec:
tls:
- secretName: {{ include "backstage.fullname" . }}-tls
hosts:
- {{ $frontendUrl.host }}
- {{ $backendUrl.host }}
- {{ $lighthouseUrl.host }}
rules:
- host: {{ $frontendUrl.host }}
http:
paths:
- path: /
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
pathType: Prefix
{{- end }}
backend:
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
service:
name: {{ include "frontend.serviceName" . }}
port:
number: 80
{{- else -}}
serviceName: {{ include "frontend.serviceName" . }}
servicePort: 80
{{- end }}
{{/* Route the backend inside the same hostname as the frontend when they are the same */}}
{{- if eq $frontendUrl.host $backendUrl.host}}
- path: /api/
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
pathType: Prefix
{{- end }}
backend:
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
service:
name: {{ include "backend.serviceName" . }}
port:
number: 80
{{- else -}}
serviceName: {{ include "backend.serviceName" . }}
servicePort: 80
{{- end }}
{{/* Route the backend through a different host */}}
{{- else -}}
- host: {{ $backendUrl.host }}
http:
paths:
- path: {{ $backendUrl.path | default "/" }}
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
pathType: Prefix
{{- end }}
backend:
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
service:
name: {{ include "backend.serviceName" . }}
port:
number: 80
{{- else -}}
serviceName: {{ include "backend.serviceName" . }}
servicePort: 80
{{- end }}
{{- end }}
{{/* Route lighthouse through a different host */}}
{{- if not ( eq $frontendUrl.host $lighthouseUrl.host ) }}
- host: {{ $lighthouseUrl.host }}
http:
paths:
- path: {{ $lighthouseUrl.path | default "/" }}
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
pathType: Prefix
{{- end }}
backend:
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
service:
name: {{ include "lighthouse.serviceName" . }}
port:
number: 80
{{- else -}}
serviceName: {{ include "lighthouse.serviceName" . }}
servicePort: 80
{{- end }}
{{- else }}
{{/* Route lighthouse by path with re-write rules when it is hosted under the same hostname */}}
---
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
apiVersion: networking.k8s.io/v1
{{- else -}}
apiVersion: networking.k8s.io/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ include "backstage.fullname" . }}-ingress-lighthouse
annotations:
{{- if .Values.issuer.email }}
cert-manager.io/cluster-issuer: {{ .Values.issuer.clusterIssuer }}
{{- end }}
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/configuration-snippet: |
if ($scheme = https) {
add_header Strict-Transport-Security "max-age=0;";
}
{{- toYaml .Values.ingress.annotations | nindent 4 }}
spec:
tls:
- secretName: {{ include "backstage.fullname" . }}-tls
hosts:
- {{ $lighthouseUrl.host }}
rules:
- host: {{ $frontendUrl.host }}
http:
paths:
- path: {{$lighthouseUrl.path}}(/|$)(.*)
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
pathType: Prefix
{{- end }}
backend:
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
service:
name: {{ include "lighthouse.serviceName" . }}
port:
number: 80
{{- else -}}
serviceName: {{ include "lighthouse.serviceName" . }}
servicePort: 80
{{- end }}
{{- end }}

Some files were not shown because too many files have changed in this diff Show More