Merge branch 'master' into link-button

This commit is contained in:
Kurt King
2023-01-18 09:33:52 -07:00
1112 changed files with 42144 additions and 8461 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-azure-devops': patch
---
Fixed `AssignedToCurrentUsersTeams` & `CreatedByCurrentUsersTeams` filter in `AzurePullRequestsPage` component.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-scaffolder': patch
---
rollback `@rjsf/validator-ajv8` to `@rjsf/validator-v6`
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-app-api': patch
---
Fixed an issue were the log redaction didn't properly escape RegExp characters.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-scaffolder-react': patch
'@backstage/plugin-scaffolder': patch
---
lock versions of `@rjsf/*-beta` packages
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-explore-backend': patch
---
Bump `msw` to `^0.49.0`
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-tech-insights-backend-module-jsonfc': patch
---
Surface the cause of the json rules engine
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend': minor
---
Added the option to overwrite files in the `targetPath` of the `template:fetch` action
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-scaffolder': minor
'@backstage/plugin-scaffolder-react': minor
---
Embed scaffolder workflow in other components
+179 -177
View File
@@ -2,197 +2,199 @@
"mode": "pre",
"tag": "next",
"initialVersions": {
"example-app": "0.2.78",
"@backstage/app-defaults": "1.0.9",
"example-backend": "0.2.78",
"@backstage/backend-app-api": "0.2.4",
"@backstage/backend-common": "0.17.0",
"@backstage/backend-defaults": "0.1.4",
"example-backend-next": "0.0.6",
"@backstage/backend-plugin-api": "0.2.0",
"@backstage/backend-tasks": "0.4.0",
"@backstage/backend-test-utils": "0.1.31",
"@backstage/catalog-client": "1.2.0",
"@backstage/catalog-model": "1.1.4",
"@backstage/cli": "0.22.0",
"example-app": "0.2.79",
"@backstage/app-defaults": "1.1.0",
"example-backend": "0.2.79",
"@backstage/backend-app-api": "0.3.0",
"@backstage/backend-common": "0.18.0",
"@backstage/backend-defaults": "0.1.5",
"example-backend-next": "0.0.7",
"@backstage/backend-plugin-api": "0.3.0",
"@backstage/backend-tasks": "0.4.1",
"@backstage/backend-test-utils": "0.1.32",
"@backstage/catalog-client": "1.3.0",
"@backstage/catalog-model": "1.1.5",
"@backstage/cli": "0.22.1",
"@backstage/cli-common": "0.1.11",
"@backstage/codemods": "0.1.42",
"@backstage/config": "1.0.5",
"@backstage/config-loader": "1.1.7",
"@backstage/core-app-api": "1.3.0",
"@backstage/core-components": "0.12.1",
"@backstage/core-plugin-api": "1.2.0",
"@backstage/create-app": "0.4.35",
"@backstage/dev-utils": "1.0.9",
"@backstage/config": "1.0.6",
"@backstage/config-loader": "1.1.8",
"@backstage/core-app-api": "1.4.0",
"@backstage/core-components": "0.12.3",
"@backstage/core-plugin-api": "1.3.0",
"@backstage/create-app": "0.4.36",
"@backstage/dev-utils": "1.0.11",
"e2e-test": "0.2.0",
"@backstage/errors": "1.1.4",
"@backstage/integration": "1.4.1",
"@backstage/integration-aws-node": "0.1.0",
"@backstage/integration-react": "1.1.7",
"@backstage/integration": "1.4.2",
"@backstage/integration-aws-node": "0.1.1",
"@backstage/integration-react": "1.1.9",
"@backstage/release-manifests": "0.0.8",
"@backstage/repo-tools": "0.1.0",
"@techdocs/cli": "1.2.4",
"techdocs-cli-embedded-app": "0.2.77",
"@backstage/test-utils": "1.2.3",
"@backstage/repo-tools": "0.1.1",
"@techdocs/cli": "1.3.0",
"techdocs-cli-embedded-app": "0.2.78",
"@backstage/test-utils": "1.2.4",
"@backstage/theme": "0.2.16",
"@backstage/types": "1.0.2",
"@backstage/version-bridge": "1.0.3",
"@backstage/plugin-adr": "0.2.4",
"@backstage/plugin-adr-backend": "0.2.4",
"@backstage/plugin-adr-common": "0.2.4",
"@backstage/plugin-airbrake": "0.3.12",
"@backstage/plugin-airbrake-backend": "0.2.12",
"@backstage/plugin-allure": "0.1.28",
"@backstage/plugin-analytics-module-ga": "0.1.23",
"@backstage/plugin-apache-airflow": "0.2.5",
"@backstage/plugin-api-docs": "0.8.12",
"@backstage/plugin-adr": "0.3.0",
"@backstage/plugin-adr-backend": "0.2.5",
"@backstage/plugin-adr-common": "0.2.5",
"@backstage/plugin-airbrake": "0.3.14",
"@backstage/plugin-airbrake-backend": "0.2.13",
"@backstage/plugin-allure": "0.1.30",
"@backstage/plugin-analytics-module-ga": "0.1.25",
"@backstage/plugin-apache-airflow": "0.2.7",
"@backstage/plugin-api-docs": "0.8.14",
"@backstage/plugin-api-docs-module-protoc-gen-doc": "0.1.0",
"@backstage/plugin-apollo-explorer": "0.1.5",
"@backstage/plugin-app-backend": "0.3.39",
"@backstage/plugin-auth-backend": "0.17.2",
"@backstage/plugin-auth-node": "0.2.8",
"@backstage/plugin-azure-devops": "0.2.3",
"@backstage/plugin-azure-devops-backend": "0.3.18",
"@backstage/plugin-apollo-explorer": "0.1.7",
"@backstage/plugin-app-backend": "0.3.40",
"@backstage/plugin-auth-backend": "0.17.3",
"@backstage/plugin-auth-node": "0.2.9",
"@backstage/plugin-azure-devops": "0.2.5",
"@backstage/plugin-azure-devops-backend": "0.3.19",
"@backstage/plugin-azure-devops-common": "0.3.0",
"@backstage/plugin-azure-sites": "0.1.1",
"@backstage/plugin-azure-sites-backend": "0.1.1",
"@backstage/plugin-azure-sites": "0.1.3",
"@backstage/plugin-azure-sites-backend": "0.1.2",
"@backstage/plugin-azure-sites-common": "0.1.0",
"@backstage/plugin-badges": "0.2.36",
"@backstage/plugin-badges-backend": "0.1.33",
"@backstage/plugin-bazaar": "0.2.1",
"@backstage/plugin-bazaar-backend": "0.2.2",
"@backstage/plugin-bitbucket-cloud-common": "0.2.2",
"@backstage/plugin-bitrise": "0.1.39",
"@backstage/plugin-catalog": "1.7.0",
"@backstage/plugin-catalog-backend": "1.6.0",
"@backstage/plugin-catalog-backend-module-aws": "0.1.12",
"@backstage/plugin-catalog-backend-module-azure": "0.1.10",
"@backstage/plugin-catalog-backend-module-bitbucket": "0.2.6",
"@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.1.6",
"@backstage/plugin-catalog-backend-module-bitbucket-server": "0.1.4",
"@backstage/plugin-catalog-backend-module-gerrit": "0.1.7",
"@backstage/plugin-catalog-backend-module-github": "0.2.2",
"@backstage/plugin-catalog-backend-module-gitlab": "0.1.10",
"@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.1.0",
"@backstage/plugin-catalog-backend-module-ldap": "0.5.6",
"@backstage/plugin-catalog-backend-module-msgraph": "0.4.5",
"@backstage/plugin-catalog-backend-module-openapi": "0.1.5",
"@backstage/plugin-catalog-common": "1.0.9",
"@internal/plugin-catalog-customized": "0.0.5",
"@backstage/plugin-catalog-graph": "0.2.24",
"@backstage/plugin-catalog-graphql": "0.3.16",
"@backstage/plugin-catalog-import": "0.9.2",
"@backstage/plugin-catalog-node": "1.3.0",
"@backstage/plugin-catalog-react": "1.2.2",
"@backstage/plugin-cicd-statistics": "0.1.14",
"@backstage/plugin-cicd-statistics-module-gitlab": "0.1.8",
"@backstage/plugin-circleci": "0.3.12",
"@backstage/plugin-cloudbuild": "0.3.12",
"@backstage/plugin-code-climate": "0.1.12",
"@backstage/plugin-code-coverage": "0.2.5",
"@backstage/plugin-code-coverage-backend": "0.2.5",
"@backstage/plugin-codescene": "0.1.7",
"@backstage/plugin-config-schema": "0.1.35",
"@backstage/plugin-cost-insights": "0.12.1",
"@backstage/plugin-badges": "0.2.38",
"@backstage/plugin-badges-backend": "0.1.34",
"@backstage/plugin-bazaar": "0.2.3",
"@backstage/plugin-bazaar-backend": "0.2.3",
"@backstage/plugin-bitbucket-cloud-common": "0.2.3",
"@backstage/plugin-bitrise": "0.1.41",
"@backstage/plugin-catalog": "1.7.2",
"@backstage/plugin-catalog-backend": "1.7.0",
"@backstage/plugin-catalog-backend-module-aws": "0.1.13",
"@backstage/plugin-catalog-backend-module-azure": "0.1.11",
"@backstage/plugin-catalog-backend-module-bitbucket": "0.2.7",
"@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.1.7",
"@backstage/plugin-catalog-backend-module-bitbucket-server": "0.1.5",
"@backstage/plugin-catalog-backend-module-gerrit": "0.1.8",
"@backstage/plugin-catalog-backend-module-github": "0.2.3",
"@backstage/plugin-catalog-backend-module-gitlab": "0.1.11",
"@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.1.1",
"@backstage/plugin-catalog-backend-module-ldap": "0.5.7",
"@backstage/plugin-catalog-backend-module-msgraph": "0.4.6",
"@backstage/plugin-catalog-backend-module-openapi": "0.1.6",
"@backstage/plugin-catalog-common": "1.0.10",
"@internal/plugin-catalog-customized": "0.0.6",
"@backstage/plugin-catalog-graph": "0.2.26",
"@backstage/plugin-catalog-graphql": "0.3.17",
"@backstage/plugin-catalog-import": "0.9.4",
"@backstage/plugin-catalog-node": "1.3.1",
"@backstage/plugin-catalog-react": "1.2.4",
"@backstage/plugin-cicd-statistics": "0.1.16",
"@backstage/plugin-cicd-statistics-module-gitlab": "0.1.10",
"@backstage/plugin-circleci": "0.3.14",
"@backstage/plugin-cloudbuild": "0.3.14",
"@backstage/plugin-code-climate": "0.1.14",
"@backstage/plugin-code-coverage": "0.2.7",
"@backstage/plugin-code-coverage-backend": "0.2.6",
"@backstage/plugin-codescene": "0.1.9",
"@backstage/plugin-config-schema": "0.1.37",
"@backstage/plugin-cost-insights": "0.12.3",
"@backstage/plugin-cost-insights-common": "0.1.1",
"@backstage/plugin-dynatrace": "1.0.2",
"@backstage/plugin-events-backend": "0.2.0",
"@backstage/plugin-events-backend-module-aws-sqs": "0.1.1",
"@backstage/plugin-events-backend-module-azure": "0.1.1",
"@backstage/plugin-events-backend-module-bitbucket-cloud": "0.1.1",
"@backstage/plugin-events-backend-module-gerrit": "0.1.1",
"@backstage/plugin-events-backend-module-github": "0.1.1",
"@backstage/plugin-events-backend-module-gitlab": "0.1.1",
"@backstage/plugin-events-backend-test-utils": "0.1.1",
"@backstage/plugin-events-node": "0.2.0",
"@internal/plugin-todo-list": "1.0.8",
"@internal/plugin-todo-list-backend": "1.0.8",
"@internal/plugin-todo-list-common": "1.0.7",
"@backstage/plugin-explore": "0.3.43",
"@backstage/plugin-explore-backend": "0.0.1",
"@backstage/plugin-dynatrace": "1.0.4",
"@backstage/plugin-events-backend": "0.2.1",
"@backstage/plugin-events-backend-module-aws-sqs": "0.1.2",
"@backstage/plugin-events-backend-module-azure": "0.1.2",
"@backstage/plugin-events-backend-module-bitbucket-cloud": "0.1.2",
"@backstage/plugin-events-backend-module-gerrit": "0.1.2",
"@backstage/plugin-events-backend-module-github": "0.1.2",
"@backstage/plugin-events-backend-module-gitlab": "0.1.2",
"@backstage/plugin-events-backend-test-utils": "0.1.2",
"@backstage/plugin-events-node": "0.2.1",
"@internal/plugin-todo-list": "1.0.9",
"@internal/plugin-todo-list-backend": "1.0.9",
"@internal/plugin-todo-list-common": "1.0.8",
"@backstage/plugin-explore": "0.3.45",
"@backstage/plugin-explore-backend": "0.0.2",
"@backstage/plugin-explore-common": "0.0.1",
"@backstage/plugin-explore-react": "0.0.24",
"@backstage/plugin-firehydrant": "0.1.29",
"@backstage/plugin-fossa": "0.2.44",
"@backstage/plugin-gcalendar": "0.3.8",
"@backstage/plugin-gcp-projects": "0.3.31",
"@backstage/plugin-git-release-manager": "0.3.25",
"@backstage/plugin-github-actions": "0.5.12",
"@backstage/plugin-github-deployments": "0.1.43",
"@backstage/plugin-github-issues": "0.2.1",
"@backstage/plugin-github-pull-requests-board": "0.1.6",
"@backstage/plugin-gitops-profiles": "0.3.30",
"@backstage/plugin-gocd": "0.1.18",
"@backstage/plugin-graphiql": "0.2.44",
"@backstage/plugin-graphql-backend": "0.1.29",
"@backstage/plugin-home": "0.4.28",
"@backstage/plugin-ilert": "0.2.1",
"@backstage/plugin-jenkins": "0.7.11",
"@backstage/plugin-jenkins-backend": "0.1.29",
"@backstage/plugin-jenkins-common": "0.1.11",
"@backstage/plugin-kafka": "0.3.12",
"@backstage/plugin-kafka-backend": "0.2.32",
"@backstage/plugin-kubernetes": "0.7.5",
"@backstage/plugin-kubernetes-backend": "0.9.0",
"@backstage/plugin-kubernetes-common": "0.5.0",
"@backstage/plugin-lighthouse": "0.3.12",
"@backstage/plugin-newrelic": "0.3.30",
"@backstage/plugin-newrelic-dashboard": "0.2.5",
"@backstage/plugin-org": "0.6.2",
"@backstage/plugin-org-react": "0.1.1",
"@backstage/plugin-pagerduty": "0.5.5",
"@backstage/plugin-periskop": "0.1.10",
"@backstage/plugin-periskop-backend": "0.1.10",
"@backstage/plugin-permission-backend": "0.5.14",
"@backstage/plugin-permission-common": "0.7.2",
"@backstage/plugin-permission-node": "0.7.2",
"@backstage/plugin-permission-react": "0.4.8",
"@backstage/plugin-playlist": "0.1.3",
"@backstage/plugin-playlist-backend": "0.2.2",
"@backstage/plugin-playlist-common": "0.1.3",
"@backstage/plugin-proxy-backend": "0.2.33",
"@backstage/plugin-rollbar": "0.4.12",
"@backstage/plugin-rollbar-backend": "0.1.36",
"@backstage/plugin-scaffolder": "1.9.0",
"@backstage/plugin-scaffolder-backend": "1.9.0",
"@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.14",
"@backstage/plugin-scaffolder-backend-module-rails": "0.4.7",
"@backstage/plugin-scaffolder-backend-module-yeoman": "0.2.12",
"@backstage/plugin-scaffolder-common": "1.2.3",
"@backstage/plugin-search": "1.0.5",
"@backstage/plugin-search-backend": "1.2.0",
"@backstage/plugin-search-backend-module-elasticsearch": "1.1.0",
"@backstage/plugin-search-backend-module-pg": "0.5.0",
"@backstage/plugin-search-backend-node": "1.1.0",
"@backstage/plugin-search-common": "1.2.0",
"@backstage/plugin-search-react": "1.3.0",
"@backstage/plugin-sentry": "0.4.5",
"@backstage/plugin-shortcuts": "0.3.4",
"@backstage/plugin-sonarqube": "0.6.0",
"@backstage/plugin-sonarqube-backend": "0.1.4",
"@backstage/plugin-sonarqube-react": "0.1.0",
"@backstage/plugin-splunk-on-call": "0.4.1",
"@backstage/plugin-stack-overflow": "0.1.8",
"@backstage/plugin-stack-overflow-backend": "0.1.8",
"@backstage/plugin-tech-insights": "0.3.4",
"@backstage/plugin-tech-insights-backend": "0.5.5",
"@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.23",
"@backstage/plugin-explore-react": "0.0.25",
"@backstage/plugin-firehydrant": "0.1.31",
"@backstage/plugin-fossa": "0.2.46",
"@backstage/plugin-gcalendar": "0.3.10",
"@backstage/plugin-gcp-projects": "0.3.33",
"@backstage/plugin-git-release-manager": "0.3.27",
"@backstage/plugin-github-actions": "0.5.14",
"@backstage/plugin-github-deployments": "0.1.45",
"@backstage/plugin-github-issues": "0.2.3",
"@backstage/plugin-github-pull-requests-board": "0.1.8",
"@backstage/plugin-gitops-profiles": "0.3.32",
"@backstage/plugin-gocd": "0.1.20",
"@backstage/plugin-graphiql": "0.2.46",
"@backstage/plugin-graphql-backend": "0.1.30",
"@backstage/plugin-home": "0.4.30",
"@backstage/plugin-ilert": "0.2.3",
"@backstage/plugin-jenkins": "0.7.13",
"@backstage/plugin-jenkins-backend": "0.1.30",
"@backstage/plugin-jenkins-common": "0.1.12",
"@backstage/plugin-kafka": "0.3.14",
"@backstage/plugin-kafka-backend": "0.2.33",
"@backstage/plugin-kubernetes": "0.7.7",
"@backstage/plugin-kubernetes-backend": "0.9.1",
"@backstage/plugin-kubernetes-common": "0.5.1",
"@backstage/plugin-lighthouse": "0.3.14",
"@backstage/plugin-newrelic": "0.3.32",
"@backstage/plugin-newrelic-dashboard": "0.2.7",
"@backstage/plugin-org": "0.6.4",
"@backstage/plugin-org-react": "0.1.3",
"@backstage/plugin-pagerduty": "0.5.7",
"@backstage/plugin-periskop": "0.1.12",
"@backstage/plugin-periskop-backend": "0.1.11",
"@backstage/plugin-permission-backend": "0.5.15",
"@backstage/plugin-permission-common": "0.7.3",
"@backstage/plugin-permission-node": "0.7.3",
"@backstage/plugin-permission-react": "0.4.9",
"@backstage/plugin-playlist": "0.1.5",
"@backstage/plugin-playlist-backend": "0.2.3",
"@backstage/plugin-playlist-common": "0.1.4",
"@backstage/plugin-proxy-backend": "0.2.34",
"@backstage/plugin-rollbar": "0.4.14",
"@backstage/plugin-rollbar-backend": "0.1.37",
"@backstage/plugin-scaffolder": "1.10.0",
"@backstage/plugin-scaffolder-backend": "1.10.0",
"@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.15",
"@backstage/plugin-scaffolder-backend-module-rails": "0.4.8",
"@backstage/plugin-scaffolder-backend-module-sentry": "0.1.0",
"@backstage/plugin-scaffolder-backend-module-yeoman": "0.2.13",
"@backstage/plugin-scaffolder-common": "1.2.4",
"@backstage/plugin-scaffolder-react": "1.0.0",
"@backstage/plugin-search": "1.0.7",
"@backstage/plugin-search-backend": "1.2.1",
"@backstage/plugin-search-backend-module-elasticsearch": "1.1.1",
"@backstage/plugin-search-backend-module-pg": "0.5.1",
"@backstage/plugin-search-backend-node": "1.1.1",
"@backstage/plugin-search-common": "1.2.1",
"@backstage/plugin-search-react": "1.4.0",
"@backstage/plugin-sentry": "0.4.7",
"@backstage/plugin-shortcuts": "0.3.6",
"@backstage/plugin-sonarqube": "0.6.2",
"@backstage/plugin-sonarqube-backend": "0.1.5",
"@backstage/plugin-sonarqube-react": "0.1.1",
"@backstage/plugin-splunk-on-call": "0.4.3",
"@backstage/plugin-stack-overflow": "0.1.10",
"@backstage/plugin-stack-overflow-backend": "0.1.9",
"@backstage/plugin-tech-insights": "0.3.6",
"@backstage/plugin-tech-insights-backend": "0.5.6",
"@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.24",
"@backstage/plugin-tech-insights-common": "0.2.9",
"@backstage/plugin-tech-insights-node": "0.3.7",
"@backstage/plugin-tech-radar": "0.5.19",
"@backstage/plugin-techdocs": "1.4.1",
"@backstage/plugin-techdocs-addons-test-utils": "1.0.7",
"@backstage/plugin-techdocs-backend": "1.5.0",
"@backstage/plugin-techdocs-module-addons-contrib": "1.0.7",
"@backstage/plugin-techdocs-node": "1.4.3",
"@backstage/plugin-techdocs-react": "1.1.0",
"@backstage/plugin-todo": "0.2.14",
"@backstage/plugin-todo-backend": "0.1.36",
"@backstage/plugin-user-settings": "0.6.0",
"@backstage/plugin-user-settings-backend": "0.1.3",
"@backstage/plugin-vault": "0.1.6",
"@backstage/plugin-vault-backend": "0.2.5",
"@backstage/plugin-xcmetrics": "0.2.32"
"@backstage/plugin-tech-insights-node": "0.3.8",
"@backstage/plugin-tech-radar": "0.6.0",
"@backstage/plugin-techdocs": "1.4.3",
"@backstage/plugin-techdocs-addons-test-utils": "1.0.9",
"@backstage/plugin-techdocs-backend": "1.5.1",
"@backstage/plugin-techdocs-module-addons-contrib": "1.0.9",
"@backstage/plugin-techdocs-node": "1.4.4",
"@backstage/plugin-techdocs-react": "1.1.2",
"@backstage/plugin-todo": "0.2.16",
"@backstage/plugin-todo-backend": "0.1.37",
"@backstage/plugin-user-settings": "0.6.2",
"@backstage/plugin-user-settings-backend": "0.1.4",
"@backstage/plugin-vault": "0.1.8",
"@backstage/plugin-vault-backend": "0.2.6",
"@backstage/plugin-xcmetrics": "0.2.34"
},
"changesets": []
}
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-adr': patch
---
Render the common `<MissingAnnotationEmptyState />` component when the `backstage.io/adr-location` annotation is missing from the component
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-catalog-backend': patch
---
Fixed an issue where internal references in the catalog would stick around for longer than expected, causing entities to not be deleted or orphaned as expected.
+5
View File
@@ -247,6 +247,11 @@ module.exports = {
{
forbid: [
{ element: 'button', message: 'use MUI <Button> instead' },
{ element: 'p', message: 'use MUI <Typography> instead' },
{
element: 'span',
message: 'use a MUI <Typography> variant instead',
},
],
},
],
+3 -3
View File
@@ -8,11 +8,11 @@ x-uffizzi:
services:
backstage:
image: '${BACKSTAGE_IMAGE}'
environment:
POSTGRES_HOST: localhost
POSTGRES_HOST: db
POSTGRES_USER: postgres
POSTGRES_PASSWORD: example
GITHUB_TOKEN: abc
NODE_ENV: production
deploy:
resources:
@@ -21,7 +21,7 @@ services:
entrypoint: '/bin/sh'
command:
- '-c'
- "APP_CONFIG_app_baseUrl=$$UFFIZZI_URL APP_CONFIG_backend_baseUrl=$$UFFIZZI_URL APP_CONFIG_auth_environment='production' node packages/backend --config app-config.yaml --config app-config.production.yaml"
- "APP_CONFIG_app_baseUrl=$$UFFIZZI_URL APP_CONFIG_backend_baseUrl=$$UFFIZZI_URL APP_CONFIG_auth_environment='production' node packages/backend --config app-config.yaml"
db:
image: postgres
@@ -2,9 +2,271 @@ app:
title: Backstage Uffizzi Environment
baseUrl: ${UFFIZZI_URL}
organization:
name: My Company
backend:
baseUrl: ${UFFIZZI_URL}
auth:
keys:
# random mock key for uffizi deployments
- secret: 5TXvdjVZFxF7qf9K5RAYRDoGrLzJooqa
listen:
port: 7007
database:
client: better-sqlite3
connection: ':memory:'
cache:
store: memory
cors:
origin: ${UFFIZZI_URL}
methods: [GET, POST, PUT, DELETE]
methods: [GET, HEAD, PATCH, POST, PUT, DELETE]
credentials: true
csp:
connect-src: ["'self'", 'http:', 'https:']
# Content-Security-Policy directives follow the Helmet format: https://helmetjs.github.io/#reference
# Default Helmet Content-Security-Policy values can be removed by setting the key to false
auth:
environment: production
providers: {}
proxy:
enabled: true
url: https://demo.backstage.io/api/auth
catalog:
locations:
- type: url
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all.yaml
- type: url
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme-corp.yaml
rules:
- allow: [User, Group]
- type: url
target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/all-templates.yaml
rules:
- allow: [Template]
proxy:
'/circleci/api':
target: https://circleci.com/api/v1.1
headers:
Circle-Token: ${CIRCLECI_AUTH_TOKEN}
'/jenkins/api':
target: http://localhost:8080
headers:
Authorization: ${JENKINS_BASIC_AUTH_HEADER}
'/travisci/api':
target: https://api.travis-ci.com
changeOrigin: true
headers:
Authorization: ${TRAVISCI_AUTH_TOKEN}
travis-api-version: '3'
'/newrelic/apm/api':
target: https://api.newrelic.com/v2
headers:
X-Api-Key: ${NEW_RELIC_REST_API_KEY}
'/newrelic/api':
target: https://api.newrelic.com
headers:
X-Api-Key: ${NEW_RELIC_USER_KEY}
'/pagerduty':
target: https://api.pagerduty.com
headers:
Authorization: Token token=${PAGERDUTY_TOKEN}
'/buildkite/api':
target: https://api.buildkite.com/v2/
headers:
Authorization: ${BUILDKITE_TOKEN}
'/sentry/api':
target: https://sentry.io/api/
allowedMethods: ['GET']
headers:
Authorization: ${SENTRY_TOKEN}
'/ilert':
target: https://api.ilert.com
allowedMethods: ['GET', 'POST', 'PUT']
allowedHeaders: ['Authorization']
headers:
Authorization: ${ILERT_AUTH_HEADER}
'/airflow':
target: https://your.airflow.instance.com/api/v1
headers:
Authorization: ${AIRFLOW_BASIC_AUTH_HEADER}
'/gocd':
target: https://your.gocd.instance.com/go/api
allowedMethods: ['GET']
allowedHeaders: ['Authorization']
headers:
Authorization: Basic ${GOCD_AUTH_CREDENTIALS}
'/dynatrace':
target: https://your.dynatrace.instance.com/api/v2
headers:
Authorization: 'Api-Token ${DYNATRACE_ACCESS_TOKEN}'
techdocs:
builder: 'local' # Alternatives - 'external'
generator:
runIn: 'docker'
# dockerImage: my-org/techdocs # use a custom docker image
# pullImage: true # or false to disable automatic pulling of image (e.g. if custom docker login is required)
publisher:
type: 'local' # Alternatives - 'googleGcs' or 'awsS3' or 'azureBlobStorage' or 'openStackSwift'. Read documentation for using alternatives.
dynatrace:
baseUrl: https://your.dynatrace.instance.com
# Score-cards sample configuration.
scorecards:
jsonDataUrl: https://raw.githubusercontent.com/Oriflame/backstage-plugins/main/plugins/score-card/sample-data/
wikiLinkTemplate: https://link-to-wiki/{id}
sentry:
organization: my-company
rollbar:
organization: my-company
# NOTE: The rollbar-backend & accountToken key may be deprecated in the future (replaced by a proxy config)
accountToken: my-rollbar-account-token
lighthouse:
baseUrl: http://localhost:3003
kubernetes:
serviceLocatorMethod:
type: 'multiTenant'
clusterLocatorMethods:
- type: 'config'
clusters: []
kafka:
clientId: backstage
clusters:
- name: cluster
dashboardUrl: https://akhq.io/
brokers:
- localhost:9092
allure:
baseUrl: http://localhost:5050/allure-docker-service
integrations:
github:
- host: github.com
token: ${GITHUB_TOKEN}
### Example for how to add your GitHub Enterprise instance using the API:
# - host: ghe.example.net
# apiBaseUrl: https://ghe.example.net/api/v3
# token: ${GHE_TOKEN}
### Example for how to add your GitHub Enterprise instance using raw HTTP fetches (token is optional):
# - host: ghe.example.net
# rawBaseUrl: https://ghe.example.net/raw
# token: ${GHE_TOKEN}
gitlab:
- host: gitlab.com
token: ${GITLAB_TOKEN}
### Example for how to add a bitbucket cloud integration
# bitbucketCloud:
# - username: ${BITBUCKET_USERNAME}
# appPassword: ${BITBUCKET_APP_PASSWORD}
### Example for how to add your bitbucket server instance using the API:
# - host: server.bitbucket.com
# apiBaseUrl: server.bitbucket.com
# username: ${BITBUCKET_SERVER_USERNAME}
# appPassword: ${BITBUCKET_SERVER_APP_PASSWORD}
azure:
- host: dev.azure.com
token: ${AZURE_TOKEN}
# googleGcs:
# clientEmail: 'example@example.com'
# privateKey: ${GCS_PRIVATE_KEY}
awsS3:
- host: amazonaws.com
accessKeyId: ${AWS_ACCESS_KEY_ID}
secretAccessKey: ${AWS_SECRET_ACCESS_KEY}
costInsights:
engineerCost: 200000
engineerThreshold: 0.5
products:
computeEngine:
name: Compute Engine
icon: compute
cloudDataflow:
name: Cloud Dataflow
icon: data
cloudStorage:
name: Cloud Storage
icon: storage
bigQuery:
name: BigQuery
icon: search
events:
name: Events
icon: data
metrics:
DAU:
name: Daily Active Users
default: true
MSC:
name: Monthly Subscribers
currencies:
engineers:
label: 'Engineers 🛠'
unit: 'engineer'
usd:
label: 'US Dollars 💵'
kind: 'USD'
unit: 'dollar'
prefix: '$'
rate: 1
carbonOffsetTons:
label: 'Carbon Offset Tons ♻️⚖️s'
kind: 'CARBON_OFFSET_TONS'
unit: 'carbon offset ton'
rate: 3.5
beers:
label: 'Beers 🍺'
kind: 'BEERS'
unit: 'beer'
rate: 4.5
pintsIceCream:
label: 'Pints of Ice Cream 🍦'
kind: 'PINTS_OF_ICE_CREAM'
unit: 'ice cream pint'
rate: 5.5
pagerduty:
eventsBaseUrl: 'https://events.pagerduty.com/v2'
jenkins:
instances:
- name: default
baseUrl: https://jenkins.example.com
username: backstage-bot
apiKey: 123456789abcdef0123456789abcedf012
azureDevOps:
host: dev.azure.com
token: my-token
organization: my-company
apacheAirflow:
baseUrl: https://your.airflow.instance.com
gocd:
baseUrl: https://your.gocd.instance.com
permission:
enabled: true
+1
View File
@@ -82,6 +82,7 @@ dependabot
deps
destructured
destructuring
Deutsche
dev
devops
devs
-6
View File
@@ -1,6 +0,0 @@
StylesPath = .
Vocab = Backstage
[*.md]
BasedOnStyles = Vale
Vale.Terms = NO
@@ -28,7 +28,7 @@ jobs:
ref: 'refs/pull/${{ github.event.pull_request.number }}/merge'
- name: fetch base
run: git fetch --depth 1 origin ${{ github.base_ref }}
- uses: backstage/actions/changeset-feedback@v0.5.12
- uses: backstage/actions/changeset-feedback@v0.5.13
name: Generate feedback
with:
diffRef: 'origin/master'
+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v7
id: stale
with:
stale-issue-message: >
+3 -25
View File
@@ -33,7 +33,7 @@ jobs:
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.12
uses: backstage/actions/yarn-install@v0.5.13
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
@@ -63,7 +63,7 @@ jobs:
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.12
uses: backstage/actions/yarn-install@v0.5.13
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
@@ -181,25 +181,14 @@ jobs:
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.12
uses: backstage/actions/yarn-install@v0.5.13
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
- name: check for yarn.lock changes
id: yarn-lock
run: git diff --quiet origin/master HEAD -- yarn.lock
continue-on-error: true
- name: lint changed packages
if: ${{ steps.yarn-lock.outcome == 'success' }}
run: yarn backstage-cli repo lint --since origin/master
- name: lint all packages
if: ${{ steps.yarn-lock.outcome == 'failure' }}
run: yarn backstage-cli repo lint
- name: test changed packages
if: ${{ steps.yarn-lock.outcome == 'success' }}
run: yarn backstage-cli repo test --maxWorkers=2 --workerIdleMemoryLimit=1300M --since origin/master
env:
BACKSTAGE_TEST_DISABLE_DOCKER: 1
@@ -207,17 +196,6 @@ jobs:
BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres9.ports[5432] }}
BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING: mysql://root:root@localhost:${{ job.services.mysql8.ports[3306] }}/ignored
- name: test all packages (and upload coverage)
if: ${{ steps.yarn-lock.outcome == 'failure' }}
run: |
yarn backstage-cli repo test --maxWorkers=2 --workerIdleMemoryLimit=800M --coverage
bash <(curl -s https://codecov.io/bash) -N $(git rev-parse FETCH_HEAD)
env:
BACKSTAGE_TEST_DISABLE_DOCKER: 1
BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres13.ports[5432] }}
BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres9.ports[5432] }}
BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING: mysql://root:root@localhost:${{ job.services.mysql8.ports[3306] }}/ignored
- name: ensure clean working directory
run: |
if files=$(git ls-files --exclude-standard --others --modified) && [[ -z "$files" ]]; then
+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
cron:
runs-on: ubuntu-latest
steps:
- uses: backstage/actions/cron@v0.5.12
- uses: backstage/actions/cron@v0.5.13
with:
app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }}
private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }}
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.12
uses: backstage/actions/yarn-install@v0.5.13
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.12
uses: backstage/actions/yarn-install@v0.5.13
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
+2 -2
View File
@@ -68,7 +68,7 @@ jobs:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.12
uses: backstage/actions/yarn-install@v0.5.13
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
@@ -145,7 +145,7 @@ jobs:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.12
uses: backstage/actions/yarn-install@v0.5.13
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
+1 -1
View File
@@ -10,4 +10,4 @@ jobs:
if: github.repository == 'backstage/backstage'
steps:
- name: Issue sync
uses: backstage/actions/issue-sync@v0.5.12
uses: backstage/actions/issue-sync@v0.5.13
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
const prNumber = artifact.name.slice('pr_number-'.length)
core.setOutput('pr-number', prNumber);
- uses: backstage/actions/re-review@v0.5.12
- uses: backstage/actions/re-review@v0.5.13
with:
app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }}
private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }}
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
if: github.repository == 'backstage/backstage' && ( github.event.pull_request || github.event.issue.pull_request )
steps:
- name: PR sync
uses: backstage/actions/pr-sync@v0.5.12
uses: backstage/actions/pr-sync@v0.5.13
with:
github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }}
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.12
uses: backstage/actions/yarn-install@v0.5.13
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
@@ -23,7 +23,7 @@ jobs:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.12
uses: backstage/actions/yarn-install@v0.5.13
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
+20 -18
View File
@@ -8,6 +8,9 @@ on:
jobs:
build-backstage:
env:
NODE_OPTIONS: --max-old-space-size=4096
UFFIZZI_URL: https://uffizzi.com
name: Build PR image
runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }}
@@ -16,8 +19,6 @@ jobs:
steps:
- name: checkout
uses: actions/checkout@v3
with:
path: backstage
- name: setup-node
uses: actions/setup-node@v3
@@ -25,21 +26,22 @@ jobs:
node-version: 16.x
registry-url: https://registry.npmjs.org/
- name: backstage create-app | delete if already exists
run: |
rm -rf ./example-app
npx @backstage/create-app
env:
BACKSTAGE_APP_NAME: example-app
- name: yarn build
run: |
yarn build:all
working-directory: ./example-app
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.13
with:
cache-prefix: linux-v16
- name: Use Uffizzi's backstage app config
run: |
cp ./backstage/.github/uffizzi/uffizzi.production.app-config.yaml ./example-app/app-config.production.yaml;
cp -f ./.github/uffizzi/uffizzi.production.app-config.yaml ./app-config.yaml
- name: typescript build
run: |
yarn tsc
- name: backstage build
run: |
yarn workspace example-backend build
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
@@ -50,16 +52,16 @@ jobs:
- name: Docker metadata
id: meta
uses: docker/metadata-action@v3
uses: docker/metadata-action@v4
with:
images: registry.uffizzi.com/${{ env.UUID_TAG_APP }}
tags: type=raw,value=60d
- name: Build Image
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
context: example-app
file: example-app/packages/backend/Dockerfile
context: .
file: packages/backend/Dockerfile
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
push: true
+1 -1
View File
@@ -52,7 +52,7 @@ jobs:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.12
uses: backstage/actions/yarn-install@v0.5.13
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
-131
View File
@@ -1,131 +0,0 @@
name: E2E Test Tugboat
on: deployment_status
jobs:
set-pending:
if: github.event.deployment_status.state != 'success' && github.event.deployment_status.state != 'failed'
name: Set pending waiting for Tugboat
runs-on: ubuntu-latest
steps:
# Set an initial commit status message to indicate that the tests are
# running.
- name: set pending status
uses: actions/github-script@v6
with:
github-token: ${{secrets.GH_SERVICE_ACCOUNT_TOKEN}}
debug: true
script: |
return github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.sha,
state: 'pending',
context: 'Backstage Tugboat E2E Tests',
description: 'Waiting for Tugboat to complete deployment',
target_url: "https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}"
});
run-tests:
# Only run after a successful Tugboat deployment.
if: github.event.deployment_status.state == 'success'
name: Run tests against Tugboat deployment
runs-on: ubuntu-latest
steps:
# Set an initial commit status message to indicate that the tests are
# running.
- name: set pending status
uses: actions/github-script@v6
with:
github-token: ${{secrets.GH_SERVICE_ACCOUNT_TOKEN}}
debug: true
script: |
return github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.sha,
state: 'pending',
context: 'Backstage Tugboat E2E Tests',
description: 'Running against tugboat preview',
target_url: "https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}"
});
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16.x'
- name: yarn install
run: yarn --cwd cypress install
# This is required because the environment_url param that Tugboat uses
# to tell us where the preview is located isn't supported unless you
# specify the custom Accept header when getting the deployment_status,
# and GitHub actions doesn't do that by default. So instead we have to
# load the status object manually and get the data we need.
# https://developer.github.com/changes/2016-04-06-deployment-and-deployment-status-enhancements/
- name: get deployment status
id: get-status-env
uses: actions/github-script@v6
with:
github-token: ${{secrets.GH_SERVICE_ACCOUNT_TOKEN}}
result-encoding: string
script: |
const result = await github.rest.repos.getDeploymentStatus({
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: context.payload.deployment.id,
status_id: context.payload.deployment_status.id,
headers: {
'Accept': 'application/vnd.github.ant-man-preview+json'
},
});
console.log(result);
return result.data.environment_url;
- name: cypress run
uses: cypress-io/github-action@v2
env:
CYPRESS_baseUrl: ${{steps.get-status-env.outputs.result}}
with:
config-file: ./cypress.json
working-directory: ./cypress
browser: chrome
install: false
headless: true
- name: update artifact
if: ${{ always() }}
uses: actions/upload-artifact@v3
with:
name: cypress-videos
path: ./cypress/cypress/videos
- name: set status
if: ${{ failure() }}
uses: actions/github-script@v6
with:
github-token: ${{secrets.GH_SERVICE_ACCOUNT_TOKEN}}
script: |
return github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.sha,
state: "error",
context: 'Backstage Tugboat E2E Tests',
target_url: "https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}"
});
- name: set status
if: ${{ success() }}
uses: actions/github-script@v6
with:
github-token: ${{secrets.GH_SERVICE_ACCOUNT_TOKEN}}
script: |
return github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.sha,
state: "success",
context: 'Backstage Tugboat E2E Tests',
target_url: "https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}"
});
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.12
uses: backstage/actions/yarn-install@v0.5.13
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.12
uses: backstage/actions/yarn-install@v0.5.13
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
- name: storybook yarn install
-17
View File
@@ -1,17 +0,0 @@
services:
backstage:
image: tugboatqa/node:lts
expose: 7007
default: true
commands:
init:
- mkdir -p /etc/service/node
- echo "#!/bin/sh" > /etc/service/node/run
- echo "yarn --cwd ${TUGBOAT_ROOT} start-backend --config ${TUGBOAT_ROOT}/app-config.yaml --config ${TUGBOAT_ROOT}/.tugboat/tugboat.app-config.production.yaml" >> /etc/service/node/run
- chmod +x /etc/service/node/run
build:
- yarn install --immutable
- yarn workspace example-app build
start:
# wget the endpoint. Will retry every 2 seconds. 30 retries = 1m for service to come up. Plenty.
- wget -O /dev/null -o /dev/null --tries=30 --timeout=5 --retry-connrefused http://localhost:7007
@@ -1,10 +0,0 @@
app:
title: Backstage Tugboat Preview
baseUrl: ${TUGBOAT_DEFAULT_SERVICE_URL}
backend:
baseUrl: ${TUGBOAT_DEFAULT_SERVICE_URL}
cors:
origin: ${TUGBOAT_DEFAULT_SERVICE_URL}
methods: [GET, POST, PUT, DELETE]
credentials: true
+4 -1
View File
@@ -1,4 +1,7 @@
StylesPath = .github/styles
StylesPath = .github/vale
Vocab = Backstage
[*.md]
BasedOnStyles = Vale
Vale.Terms = NO
+4
View File
@@ -224,3 +224,7 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/
| [FanDuel](https://fanduel.com) | [Diego Herrera](https://github.com/diegoh), [Christy Campbell](https://github.com/FD-ChristopherCampbell) | We use backstage as our developer portal to provide visibility of our software, ownership, strategy, and the state of maturity across disciplines. |
| [Operate First](https://www.operate-first.cloud/) | [Tom Coufal](https://github.com/tumido), [Sam Kopecky](https://github.com/samokopecky) | Backstage provides us with a public service catalog and serves as a gateway to our community cloud. Our instance is publicly available to everyone [here](https://service-catalog.operate-first.cloud/) ([source](https://github.com/operate-first/service-catalog)) |
| [Tractable AI](https://tractable.ai/) | [Stephan Schielke](https://github.com/stephanschielke) | We are hitting a critical point in our scale (100+ engineers) and need to get a handle on discoverability and ownership. The Service Catalog, TechDocs and Search are essential to us to achieve that. |
| [Garanti BBVA Teknoloji](https://www.linkedin.com/company/garanti-teknoloji/) | [Caglar Cataloglu](https://github.com/crozwise) | We are using Backstage focusing on improving experience of developers, minimizing friction from idea to production. We call our portal as "Hyperspace" and very excited for our community (2000+ engineers) that finally we have a platform to boost our productivity!
| [Booking.com](https://www.linkedin.com/company/booking.com/) | [Mesut Yilmazyildirim](https://www.linkedin.com/in/myilmazyildirim) | We are adopting Backstage as the new reliability platform inside the company. We are migrating UIs of our internal developer tools to Backstage for a better user experience.
| [Swissquote Bank](https://swissquote.com/company/jobs/open-positions) | [Bruno Rocha](https://www.linkedin.com/in/bruno-rocha1/) | Integrating Backstage as the visualization layer & tactical overview of our services and teams.
| [XP Inc.](https://www.linkedin.com/company/xpinc/) | [Gabriel Santos](https://www.linkedin.com/in/gabriel-santos-6bb740a/) | Developer Portal Catalog (components, APIs, resources, users, groups) and organization relations.
+1 -1
View File
@@ -142,7 +142,7 @@ Changesets **are** needed for new packages, as that is what triggers the package
6. Push the commit with your changeset to the branch associated with your PR
7. Accept our gratitude for making the release process easier on the maintainers
For more information, checkout [adding a changeset](https://github.com/atlassian/changesets/blob/master/docs/adding-a-changeset.md) documentation in the changesets repository.
For more information, check out [adding a changeset](https://github.com/atlassian/changesets/blob/master/docs/adding-a-changeset.md) documentation in the changesets repository.
## Merging to Master
-4
View File
@@ -2,10 +2,6 @@
Hey 👋 Welcome to the Cypress tests for Backstage. They're designed to be run against the `packages/app` folder in the main repo, and be some form of smoke tests to make sure that we don't break any core functionality.
They run part of the PR build, and are triggered from the `.github/workflows/tugboat.yml` file.
The main app gets built up part of a [Tugboat Build](https://tugboat.qa), which when complete, sends a `deployment event` to the PR triggering the aforementioned workflow.
### Running Locally
In order to make typescript happy, this `cypress` package is separate from all the Jest dependencies in the monorepo workspaces setup.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 263 KiB

+4
View File
@@ -37,6 +37,8 @@ auth:
audience: ${AUTH_AUTH0_AUDIENCE}
connection: ${AUTH_AUTH0_CONNECTION}
connectionScope: ${AUTH_AUTH0_CONNECTION_SCOPE}
session:
secret: ${AUTH_SESSION_SECRET}
```
The Auth0 provider is a structure with three configuration keys:
@@ -46,6 +48,8 @@ The Auth0 provider is a structure with three configuration keys:
page
- `domain`: The Application domain, found on the Auth0 Application page
Because Auth0 requires a session you need to give the session a secret key.
## Optional Configuration
- `audience`: The intended recipients of the token
+1 -1
View File
@@ -18,7 +18,7 @@ Settings for local development:
- Name: Backstage (or your custom app name)
- Redirect URI: `http://localhost:7007/api/auth/gitlab/handler/frame`
- Scopes: `read_user`
- Scopes: `read_api` and `read_user`
## Configuration
+3
View File
@@ -110,6 +110,9 @@ value of the configured key as the secret. It must also have the following paylo
- `sub`: "backstage-server" (only this value supported currently)
- `exp`: one hour from the time it was generated, in epoch seconds
> NOTE: The JWT must encode the `alg` header as a protected header, such as with
> [setProtectedHeader](https://github.com/panva/jose/blob/main/docs/classes/jwt_sign.SignJWT.md#setprotectedheader).
## Granular Access Control
We plan to build out the service-to-service auth to be much more powerful in the
@@ -0,0 +1,70 @@
---
id: index
title: Backend System Architecture
sidebar_label: System Architecture
# prettier-ignore
description: The structure and architecture of the new Backend System and its component parts
---
# Overview
This section introduces the high-level building blocks upon which this new
system is built. These are all concepts that exist in our current system in one
way or another, but they have all been lifted up to be first class concerns in
the new system.
## Building Blocks
This section introduces the high-level building blocks upon which this new system is built. These are all concepts that exist in our current system in one way or another, but they have all been lifted up to be first class concerns in the new system.
<!-- TODO: GRAPH GO HERE -->
### Backend
This is the backend instance itself, which you can think of as the unit of deployment. It does not have any functionality in and of itself, but is simply responsible for wiring things together.
It is up to you to decide how many different backends you want to deploy. You can have all features in a single one, or split things out into multiple smaller deployments. All depending on your need to scale and isolate individual features.
### Plugins
Plugins provide the actual features, just like in our existing system. They operate completely independently of each other. If plugins want to communicate with each other, they must do so over the wire. There can be no direct communication between plugins through code. Because of this constraint, each plugin can be considered to be its own microservice.
### Services
Services provide utilities to help make it simpler to implement plugins, so that each plugin doesn't need to implement everything from scratch. There are both many built-in services, like the ones for logging, database access, and reading configuration, but you can also import third-party services, or create your own.
Services are also a customization point for individual backend installations. You can both override services with your own implementations, as well as make smaller customizations to existing services.
### Extension Points
Many plugins have ways in which you can extend them, for example entity providers for the Catalog, or custom actions for the Scaffolder. These extension patterns are now encoded into Extension Points.
Extension Points look a little bit like services, since you depended on them just like you would a service. A key difference is that extension points are registered and provided by plugins themselves, based on what customizations each individual plugin wants to expose.
Extension Points are also exported separately from the plugin instance itself, and a single plugin can also expose multiple different extension points at once. This makes it easier to evolve and deprecated individual Extension Points over time, rather than dealing with a single large API surface.
### Modules
Modules use the plugin Extension Points to add new features for plugins. They might for example add an individual Catalog Entity Provider, or one or more Scaffolder Actions. Modules are basically plugins for plugins.
Each module may only extend a single plugin, and the module must be deployed together with that plugin in the same backend instance. Modules may however only communicate with their plugin through its registered extension points.
Just like plugins, modules also have access to services and can depend on their own service implementations. They will however share services with the plugin that they extend, there are no module-specific service implementations.
## Package structure
A detailed explanation of the package architecture can be found in the
[Backstage Architecture
Overview](../../overview/architecture-overview.md#package-architecture). The
most important packages to consider for this system are `backend`,
`plugin-<pluginId>-backend`, `plugin-<pluginId>-node`, and
`plugin-<pluginId>-backend-module-<moduleId>`.
- `plugin-<pluginId>-backend` houses the implementation of the backend plugins
themselves.
- `plugin-<pluginId>-node` houses the extension points and any other utilities
that modules or other plugins might need.
- `plugin-<pluginId>-backend-module-<moduleId>` houses the modules that extend
the plugins via the extension points.
- `backend` is the backend itself that wires everything together to something
that you can deploy.
@@ -0,0 +1,27 @@
---
id: backends
title: Backend Instances
sidebar_label: Backend
# prettier-ignore
description: Service APIs for backend plugins
---
The new Backstage backend system is being built to help make it simpler to install backend plugins and to keep projects up to date. It also changes the foundation to one that makes it a lot easier to evolve plugins and the system itself with minimal disruption or cause for breaking changes. You can read more about the reasoning in the [original RFC](https://github.com/backstage/backstage/issues/11611).
One of the goals of the new system was to reduce the code needed for setting up a Backstage backend and installing plugins. This is an example of how you create, add features, and start up your backend in the new system:
```ts
import { createBackend } from '@backstage/backend-defaults';
import { catalogPlugin } from '@backstage/plugin-catalog-backend';
// Create your backend instance
const backend = createBackend();
// Install all desired features
backend.add(catalogPlugin());
// Start up the backend
await backend.start();
```
One notable change that helped achieve this much slimmer backend setup is the introduction of a system for dependency injection, which is very similar to the one in the Backstage frontend.
@@ -0,0 +1,103 @@
---
id: services
title: Backend Service APIs
sidebar_label: Service APIs
# prettier-ignore
description: Service APIs for backend plugins
---
## Backend Services
The default backend provides several [core services](https://github.com/backstage/backstage/blob/master/packages/backend-plugin-api/src/services/definitions/coreServices.ts) out of the box which includes access to configuration, logging, databases and more.
Service dependencies are declared using their `ServiceRef`s in the `deps` section of the plugin or module, and the implementations are then forwarded to the `init` method of the plugin or module.
### Service References
A `ServiceRef` is a named reference to an interface which are later used to resolve the concrete service implementation. Conceptually this is very similar to `ApiRef`s in the frontend.
Services is what provides common utilities that previously resided in the `PluginEnvironment` such as Config, Logging and Database.
On startup the backend will make sure that the services are initialized before being passed to the plugin/module that depend on them.
ServiceRefs contain a scope which is used to determine if the serviceFactory creating the service will create a new instance scoped per plugin/module or if it will be shared. `plugin` scoped services will be created once per plugin/module and `root` scoped services will be created once per backend instance.
#### Defining a Service
```ts
import {
createServiceFactory,
coreServices,
} from '@backstage/backend-plugin-api';
import { ExampleImpl } from './ExampleImpl';
export interface ExampleApi {
doSomething(): Promise<void>;
}
export const exampleServiceRef = createServiceRef<ExampleApi>({
id: 'example',
scope: 'plugin', // can be 'root' or 'plugin'
// The defaultFactory is optional to implement but it will be used if no
// other factory is provided to the backend. This allows for the backend
// to provide a default implementation of the service without having to wire
// it beforehand.
defaultFactory: async service =>
createServiceFactory({
service,
deps: {
logger: coreServices.logger,
plugin: coreServices.pluginMetadata,
},
// This root context method is only available for plugin scoped services.
// It's only called once per backend instance.
// Logger is available at the root context level as it's also a root
// scoped service.
createRootContext({ logger }) {
return new ExampleImplFactory({ logger });
},
// Plugin is available as it's a plugin scoped service and will be
// created once per plugin. The logger can be had here too if needed.
// Both this anc the root context can also be async.
factory({ logger, plugin }, rootContext) {
// This block will be executed once for every plugin that depends on
// this service
logger.info('Initializing example service plugin instance');
return rootContext.forPlugin(plugin.getId());
},
}),
});
```
### Overriding Services
In this example we replace the default root logger service implementation with a custom one that streams logs to GCP. The `rootLoggerServiceRef` has a `'root'` scope, meaning there are no plugin-specific instances of this service.
```ts
import {
createServiceFactory,
rootLoggerServiceRef,
LoggerService,
} from '@backstage/backend-plugin-api';
// This custom implementation would typically live separately from
// the backend setup code, either nearby such as in
// packages/backend/src/services/logger/GoogleCloudLogger.ts
// Or you can let it live in its own library package.
class GoogleCloudLogger implements LoggerService {
static factory = createServiceFactory({
service: rootLoggerServiceRef,
deps: {},
factory() {
return new GoogleCloudLogger();
},
});
// custom implementation here ...
}
// packages/backend/src/index.ts
const backend = createBackend({
services: [
// supplies additional or replacement services to the backend
GoogleCloudLogger.factory(),
],
});
```
@@ -0,0 +1,68 @@
---
id: plugins
title: Backend Plugins
sidebar_label: Plugins
# prettier-ignore
description: Backend plugins
---
## Creating Plugins
Plugins are created using the `createBackendPlugin` function. All plugins must have an ID and a register method. Plugins may also accept an options object, which can be either optional or required. The options are passed to the second parameter of the register method, and the options type is inferred and forwarded to the returned plugin factory function.
```ts
import {
configServiceRef,
coreServices,
createBackendPlugin,
} from '@backstage/backend-plugin-api';
// export type ExamplePluginOptions = { exampleOption: boolean };
export const examplePlugin = createBackendPlugin({
// unique id for the plugin
id: 'example',
// It's possible to provide options to the plugin
// register(env, options: ExamplePluginOptions) {
register(env) {
env.registerInit({
deps: {
logger: coreServices.logger,
},
// logger is provided by the backend based on the dependency on loggerServiceRef above.
async init({ logger }) {
logger.info('Hello from example plugin');
},
});
},
});
```
The plugin can then be installed in the backend using the returned plugin factory function:
```ts
backend.add(examplePlugin());
```
If we wanted our plugin to accept options as well, we'd accept the options as the second parameter of the register method:
```ts
export const examplePlugin = createBackendPlugin({
id: 'example',
register(env, options?: { silent?: boolean }) {
env.registerInit({
deps: { logger: coreServices.logger },
async init({ logger }) {
if (!options?.silent) {
logger.info('Hello from example plugin');
}
},
});
},
});
```
Passing the option to the plugin during installation looks like this:
```ts
backend.add(examplePlugin({ silent: true }));
```
@@ -0,0 +1,30 @@
---
id: extension-points
title: Backend Plugin Extension Points
sidebar_label: Extension Points
# prettier-ignore
description: Extension points of backend plugins
---
### Extension Points
Modules depend on extension points just as a regular dependency by specifying it in the `deps` section.
#### Defining an Extension Point
```ts
import { createExtensionPoint } from '@backstage/backend-plugin-api';
export interface ScaffolderActionsExtensionPoint {
addAction(action: ScaffolderAction): void;
}
export const scaffolderActionsExtensionPoint =
createExtensionPoint<ScaffolderActionsExtensionPoint>({
id: 'scaffolder.actions',
});
```
#### Registering an Extension Point
Extension points are registered by a plugin and extended by modules.
@@ -0,0 +1,40 @@
---
id: modules
title: Plugin Modules
sidebar_label: Modules
# prettier-ignore
description: Modules for backend plugins
---
## Creating Modules
Some facts about modules
- A Module is able to extend a plugin with additional functionality using the `ExtensionPoint`s registered by the plugin.
- A module can only extend one plugin but can interact with multiple `ExtensionPoint`s registered by that plugin.
- A module is always initialized before the plugin it extends.
A module depends on the `ExtensionPoint`s exported by the target plugin's library package, for example `@backstage/plugin-catalog-node`, and does not directly declare a dependency on the plugin package itself.
Here's an example on how to create a module that adds a new processor using the `catalogProcessingExtensionPoint`:
```ts
import { createBackendModule } from '@backstage/backend-plugin-api';
import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node';
import { MyCustomProcessor } from './processor';
export const exampleCustomProcessorCatalogModule = createBackendModule({
moduleId: 'exampleCustomProcessor',
pluginId: 'catalog',
register(env) {
env.registerInit({
deps: {
catalog: catalogProcessingExtensionPoint,
},
async init({ catalog }) {
catalog.addProcessor(new MyCustomProcessor());
},
});
},
});
```
@@ -0,0 +1,12 @@
---
id: index
title: Building Backends
sidebar_label: Overview
# prettier-ignore
description: Building backends using the new backend system
---
> NOTE: If you have an existing backend that is not yet using the new backend
> system, see [migrating](./08-migrating.md).
# Overview
@@ -0,0 +1,9 @@
---
id: migrating
title: Migrating your Backend to the New Backend System
sidebar_label: Migration Guide
# prettier-ignore
description: How to migrate existing backends to the new backend system
---
# Overview
@@ -0,0 +1,12 @@
---
id: index
title: Building Backend Plugins and Modules
sidebar_label: Overview
# prettier-ignore
description: Building backend plugins and modules using the new backend system
---
> NOTE: If you have an existing backend and/or backend plugins that are not yet
> using the new backend system, see [migrating](./08-migrating.md).
# Overview
@@ -0,0 +1,26 @@
---
id: testing
title: Testing Backend Plugins and Modules
sidebar_label: Testing
# prettier-ignore
description: Learn how to test your backend plugins and modules
---
Utilities for testing backend plugins and modules are available in `@backstage/backend-test-utils`.
`startTestBackend` returns a server which can be used together with `supertest` to test the plugins.
```ts
import { startTestBackend } from '@backstage/backend-test-utils';
import request from 'supertest';
describe('My plugin tests', () => {
it('should return 200', async () => {
const { server } = await startTestBackend({
features: [myPlugin()],
});
const response = await request(server).get('/api/example/hello');
expect(response.status).toBe(200);
});
});
```
@@ -0,0 +1,9 @@
---
id: migrating
title: Migrating your Backend Plugin to the New Backend System
sidebar_label: Migration Guide
# prettier-ignore
description: How to migrate existing backend plugins to the new backend system
---
# Overview
@@ -0,0 +1,76 @@
---
id: index
title: Core Backend Service APIs
sidebar_label: Core Services
# prettier-ignore
description: Core backend service APIs
---
The default backend provides several [core services](https://github.com/backstage/backstage/blob/master/packages/backend-plugin-api/src/services/definitions/coreServices.ts) out of the box which includes access to configuration, logging, URL Readers, databases and more.
All core services are available through the `coreServices` namespace in the `@backstage/backend-plugin-api` package.
```ts
import { coreServices } from '@backstage/backend-plugin-api';
```
## HTTP Router Service
One of the most common services is the HTTP router service which is used to expose HTTP endpoints for other plugins to consume.
The following example shows how to register a HTTP router for the `example` plugin.
This single route will be available at the `/api/example/hello` path.
```ts
import {
coreServices,
createBackendPlugin,
} from '@backstage/backend-plugin-api';
import { Router } from 'express';
createBackendPlugin({
id: 'example',
register(env) {
env.registerInit({
deps: { http: coreServices.httpRouter },
async init({ http }) {
const router = Router();
router.get('/hello', (_req, res) => {
res.status(200).json({ hello: 'world' });
});
// Registers the router at the /api/example path
http.use(router);
},
});
},
});
```
## Logging and Configuration Service
It is common for plugins to need access to configuration values and log messages.
```ts
import {
coreServices,
createBackendPlugin,
} from '@backstage/backend-plugin-api';
import { Router } from 'express';
createBackendPlugin({
id: 'example',
register(env) {
env.registerInit({
deps: {
log: coreServices.logger,
config: coreServices.config,
},
async init({ config, log }) {
log.warn('Brace yourself for more log output');
const url = config.getString('backend.baseUrl');
log.info(`Backend URL is running on ${url}`);
},
});
},
});
```
+15
View File
@@ -0,0 +1,15 @@
---
id: index
title: The Backend System
sidebar_label: Overview
# prettier-ignore
description: The backend system
---
> **DISCLAIMER: The new backend system is under active development and is not considered stable**
## Status
The new backend system is under active development, and only a small number of plugins have been migrated so far. It is possible to try it out, but it is not recommended to use this new system in production yet.
You can find an example backend setup in [the backend-next package](https://github.com/backstage/backstage/tree/master/packages/backend-next).
+1 -1
View File
@@ -17,7 +17,7 @@ into multiple different services, each running a different set of plugins. This
is a more advanced approach and requires you to be able to route requests to
the appropriate backends based on the plugin ID. Both for ingress, but also
internal traffic between Backstage backends, which is done by creating a custom
implementation of the [PluginEndpointDiscover](../reference/backend-common.pluginendpointdiscovery.md) interface.
implementation of the [DiscoveryService](../reference/backend-plugin-api.discoveryservice.md) interface.
Lastly, you can also replicate the Backstage deployments across multiple regions.
This is not a pattern that there is built-in support for and typically only makes
+7 -75
View File
@@ -23,7 +23,8 @@ See the more detailed [architecture](./architecture.md) and [tech stack](./archi
## Project roadmap
No current plans.
No current plans. Check [Backstage issues labeled `search`](https://github.com/backstage/backstage/issues?q=is%3Aopen+is%3Aissue+label%3Asearch)
for community-led ideas and initiatives.
## Supported
@@ -42,11 +43,11 @@ The following sections show the plugins and search engines currently supported b
See [Backstage Search Architecture](architecture.md) to get an overview of how
the search engines are used.
| Search Engines | Support Status |
| -------------------------------------------------- | -------------- |
| [ElasticSearch](./search-engines.md#elasticsearch) | ✅ |
| [Lunr](./search-engines.md#lunr) | ✅ |
| [Postgres](./search-engines.md#postgres) | Community ✅ |
| Search Engines | Support Status |
| ------------------------------------------------------------- | -------------- |
| [Elasticsearch/OpenSearch](./search-engines.md#elasticsearch) | ✅ |
| [Lunr](./search-engines.md#lunr) | ✅ |
| [Postgres](./search-engines.md#postgres) | Community ✅ |
[Reach out to us](#get-involved) if you want to chat about support for more plugin integrations and
search engines.
@@ -56,72 +57,3 @@ search engines.
For any questions, feedback, or to help move search forward, reach out to us in
the **#search** channel of our
[Discord chatroom](https://github.com/backstage/backstage#community).
## Use Cases supported
#### **Backstage Search Pre-Alpha**
Search Frontend letting you search through the entities of the software catalog.
The pre-alpha is intended to solve for the following user stories, but will get
there by means of a front-end only, non-extensible MVP.
- As a software engineer I should be able to navigate to a search page and
search for entities registered in the Software Catalog.
- As a software engineer I should be able to use the search input field in the
sidebar to search for entities registered in the Software Catalog.
- As a software engineer I should be able to see the number of results my search
returned.
- As a software engineer I should be able to filter on metadata (kind,
lifecycle) when Ive performed a search.
- As a software engineer I should be able to hide the filters if I dont need to
use them.
#### **Backstage Search Alpha**
Basic “out-of-the-box” in-memory indexing process of entities, and their metadata, registered to the Software Catalog.
We will consider Backstage Search to be in alpha when the above use-cases are
met, but built on top of a flexible, extensible platform.
- As an integrator, I should be able to provide all of the pre-alpha experiences
to my users if I choose, but also be able to customize the experience using a
composable set of components.
- As a plugin developer, I should have a standard way to expose my plugin's data
to Backstage Search.
- As an integrator, I should still be able to expose everything in the Software
Catalog in search, but it should be possible to customize what is searchable.
- As an integrator, although I should be able to customize all of the above, it
should be possible to have the pre-alpha user experiences covered without
having to set up and configure a search engine.
#### **Backstage Search Beta**
At least one production-ready search engine that supports the same use-cases as in the alpha.
We will consider Backstage Search to be in a beta phase when the above use-cases
are met, and can be deployed using a production-ready search engine.
- As an integrator, I should be able to power my Backstage Search experience
(including querying and indexing) using a production-ready search engine like
ElasticSearch.
- As an integrator, I should be able to configure the connection to my search
engine in **app_config.yaml**.
- As an integrator, I should be able to tune the queries sent to my chosen
search engine according to my organization's needs, but a sensible default
query should be in place so that I am not required to do so.
#### **Backstage Search 1.0**
A stable Search API for plugin developers to add search to their plugins, and app integrators to expose that to their users.
We will consider Backstage Search to be 1.0 when the above
use-cases are met, and an ecosystem of search-enabled plugins are available and
stable.
- As a plugin developer, there should be at least one example of a Backstage
plugin that integrates with search that I can use as inspiration for my own
plugin's search capabilities (for example, the TechDocs plugin).
- As an app integrator, there should be plenty of examples and documentation on
how to customize and extend search in my Backstage instance to meet my
organization's needs.
+1 -1
View File
@@ -23,7 +23,7 @@ To get started, you should get familiar with these core concepts:
Backstage Search isn't a search engine itself, rather, it provides an interface
between your Backstage instance and a Search Engine of your choice. More
concretely, a `SearchEngine` is an interface whose concrete implementations
facilitate communication with different search engines (like ElasticSearch,
facilitate communication with different search engines (like Elasticsearch,
Lunr, Solr, etc). This abstraction exists in order to support your
organization's needs.
+1 -1
View File
@@ -286,7 +286,7 @@ an example:
Backstage Search isn't a search engine itself, rather, it provides an interface
between your Backstage instance and a
[Search Engine](./concepts.md#search-engines) of your choice. Currently, we only
support two engines, an in-memory search Engine called Lunr and ElasticSearch.
support two engines, an in-memory search Engine called Lunr and Elasticsearch.
See [Search Engines](./search-engines.md) documentation for more information how
to configure these in your Backstage instance.
+43 -30
View File
@@ -5,7 +5,7 @@ description: Choosing and configuring your search engine for Backstage
---
Backstage supports 3 search engines by default, an in-memory engine called Lunr,
ElasticSearch and Postgres. You can configure your own search engines by
Elasticsearch and Postgres. You can configure your own search engines by
implementing the provided interface as mentioned in the
[search backend documentation.](./getting-started.md#Backend)
@@ -34,11 +34,16 @@ const searchEngine = new LunrSearchEngine({ logger: env.logger });
const indexBuilder = new IndexBuilder({ logger: env.logger, searchEngine });
```
> Note: Lunr is appropriate as a zero-config search engine when developing
> other parts of Backstage locally, however its use is highly discouraged when
> running Backstage in production. When deploying Backstage, use one of the
> other search engines instead.
## Postgres
The Postgres based search engine only requires that postgres being configured as
the database engine for Backstage. Therefore it targets setups that want to
avoid maintaining another external service like elastic search. The search
avoid maintaining another external service like Elasticsearch. The search
provides decent results and performs well with ten thousands of indexed
documents. The connection to postgres is established via the database manager
also used by other plugins.
@@ -91,13 +96,13 @@ search:
The Postgres documentation on [Highlighting Results](https://www.postgresql.org/docs/current/textsearch-controls.html#TEXTSEARCH-HEADLINE) has more details.
## ElasticSearch
## Elasticsearch
Backstage supports ElasticSearch search engine connections, indexing and
querying out of the box. Available configuration options enable usage of either
AWS or Elastic.co hosted solutions, or a custom self-hosted solution.
Backstage supports Elasticsearch (and OpenSearch) search engine connections,
indexing and querying out of the box. Available configuration options enable
usage of either AWS or Elastic.co hosted solutions, or a custom self-hosted solution.
Similarly to Lunr above, ElasticSearch can be set up like this:
Similarly to Lunr above, Elasticsearch can be set up like this:
```typescript
// app/backend/src/plugins/search.ts
@@ -111,40 +116,48 @@ const indexBuilder = new IndexBuilder({ logger: env.logger, searchEngine });
For the engine to be available, your backend package needs a dependency on
package `@backstage/plugin-search-backend-module-elasticsearch`.
ElasticSearch needs some additional configuration before it is ready to use
Elasticsearch needs some additional configuration before it is ready to use
within your instance. The configuration options are documented in the
[configuration schema definition file.](https://github.com/backstage/backstage/blob/master/plugins/search-backend-module-elasticsearch/config.d.ts)
The underlying functionality uses either the official ElasticSearch client
version 7.x (meaning that ElasticSearch version 7 is the only one confirmed to
The underlying functionality uses either the official Elasticsearch client
version 7.x (meaning that Elasticsearch version 7 is the only one confirmed to
be supported), or the OpenSearch client, when the `aws` or `opensearch `provider
is configured.
Should you need to create your own bespoke search experiences that require more
than just a query translator (such as faceted search or Relay pagination), you
can access the configuration of the search engine in order to create new elastic
search clients. The version of the client need not be the same as one used
internally by the elastic search engine plugin. For example:
can access the configuration of the search engine in order to create new
Elasticsearch clients. The version of the client need not be the same as one
used internally by the Elasticsearch engine plugin. For example:
```typescript
import { isOpenSearchCompatible } from '@backstage/plugin-search-backend-module-elasticsearch';
import { Client as ElasticClient } from '@elastic/elastic-search';
import { Client as ElasticClient } from '@elastic/elasticsearch';
import { Client as OpenSearchClient } from '@opensearch-project/opensearch';
const client = searchEngine.newClient(options => {
// In reality, you would only import / instantiate one of the following, but
// for illustrative purposes, here are both:
if (isOpenSearchCompatible(options)) {
return new OpenSearchClient(options);
} else {
// Return an Elasticsearch client
const esClient = searchEngine.newClient<ElasticClient>(options => {
if (!isOpenSearchCompatible(options)) {
return new ElasticClient(options);
}
throw new Error('Incompatible options');
});
// Return an OpenSearch client
const osClient = searchEngine.newClient<OpenSearchClient>(options => {
if (isOpenSearchCompatible(options)) {
return new OpenSearchClient(options);
}
throw new Error('Incompatible options');
});
```
#### Set custom index template
The elasticsearch engine gives you the ability to set a custom index template if needed.
The Elasticsearch engine gives you the ability to set a custom index template if needed.
> Index templates define settings, mappings, and aliases that can be applied automatically to new indices.
@@ -171,8 +184,8 @@ searchEngine.setIndexTemplate({
### AWS
Using AWS hosted ElasticSearch the only configuration option needed is the URL
to the ElasticSearch service. The implementation assumes that environment
Using AWS hosted Elasticsearch the only configuration option needed is the URL
to the Elasticsearch service. The implementation assumes that environment
variables for AWS access key id and secret access key are defined in accordance
to the
[default AWS credential chain.](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html).
@@ -186,8 +199,8 @@ search:
### Elastic.co
Elastic Cloud hosted ElasticSearch uses a Cloud ID to determine the instance of
hosted ElasticSearch to connect to. Additionally, username and password needs to
Elastic Cloud hosted Elasticsearch uses a Cloud ID to determine the instance of
hosted Elasticsearch to connect to. Additionally, username and password needs to
be provided either directly or using environment variables like defined in
[Backstage documentation.](https://backstage.io/docs/conf/writing#includes-and-dynamic-data)
@@ -217,8 +230,8 @@ search:
### Others
Other ElasticSearch instances can be connected to by using standard
ElasticSearch authentication methods and exposed URL, provided that the cluster
Other Elasticsearch instances can be connected to by using standard
Elasticsearch authentication methods and exposed URL, provided that the cluster
supports that. The configuration options needed are the URL to the node and
authentication information. Authentication can be handled by either providing
username/password or an API key. For more information how to create an API key,
@@ -248,13 +261,13 @@ search:
apiKey: base64EncodedKey
```
### Elastic search batch size
### Elasticsearch batch size
Default batch size of the elastic search engine is set to 1000. If you are using a lower spec computing resources (like AWS small instance),
Default batch size of the Elasticsearch engine is set to 1000. If you are using a lower spec computing resources (like AWS small instance),
you may get an error caused by limited `thread_pool` configuration. ( `429 Too Many Requests /_bulk` )
In this case you need to decrease the batch size to index the resources to prevent this kind of error. You can easily decrease
or increase the batch size in your `app-config.yaml` using the `batchSize` option provided for elasticsearch configuration.
or increase the batch size in your `app-config.yaml` using the `batchSize` option provided for Elasticsearch configuration.
#### Configuration example
+54
View File
@@ -154,6 +154,29 @@ Some more real world usable examples:
`/entities?fields=kind,metadata.namespace,metadata.name`
### Ordering
By default the entities are returned in an undefined, but stable order. You can
pass in one or more `order` query parameters to affect that ordering.
Each parameter starts either with `asc:` for ascending lexicographical order or
`desc:` for descending (reverse) lexicographical order, followed by a
dot-separated path into an entity's keys. The ordering is case insensitive. If
more than one order directive is given, later directives have lower precedence
(they are applied only when directives of higher precedence have equal values).
Example:
```text
/entities?order=asc:kind&order=desc:metadata.name
```
This will order the output first by kind ascending, and then within each kind
(if there's more than one of a given kind) by their name descending. When given
a field that does NOT exist on all entities in the result set, those entities
that do not have the field will always be sorted last in that particular order
step, no matter what the desired order was.
#### Pagination
You may pass the `offset` and `limit` query parameters to do classical
@@ -223,6 +246,37 @@ value. These are special in that they form the entity's unique
The return type is JSON, as a single [`Entity`](descriptor-format.md), or a 404
error if there was no entity with that reference triplet.
### `POST /entities/by-refs`
Gets a batch of entities by their entity refs. This is useful in contexts where
you want to fetch a large number of specific entities efficiently, for example
in GraphQL resolvers.
The request body is JSON, on the form
```json
{
"entityRefs": ["component:default/foo", "api:default/bar"],
"fields": ["kind", "metadata.name"]
}
```
where each `entityRefs` entry is an entity ref that you want to fetch. The
`fields` array is optional and works the same way as the `GET /entities` fields
above, e.g. it's used to fetch only certain slices of each entity.
The return type is JSON, on the form
```json
{
"items": [{ "kind": "Component", "metadata": { "name": "foo" } }, null]
}
```
where the `items` array has _the same length_ and _the same order_ as the input
`entityRefs` array. Each element contains the corresponding entity data, or
`null` if no entity existed in the catalog with that ref.
## Locations
TODO
@@ -251,6 +251,23 @@ the entity belongs to the `"default"` namespace.
Namespaces may also be part of the catalog, and are `v1` / `Namespace` entities,
i.e. not Backstage specific but the same as in Kubernetes.
### `uid` [output]
Each entity gets an automatically generated globally unique ID when it first
enters the database. This field is not meant to be specified as input data, but
is rater created by the database engine itself when producing the output entity.
Note that `uid` values are _not_ to be seen as stable, and should _not_ be used
as external references to an entity. The `uid` can change over time even when a
human observer might think that it wouldn't. As one of many examples,
unregistering and re-registering the exact same file will result in a different
`uid` value even though everything else is the same. Therefore there is very
little, if any, reason to read or use this field externally.
If you want to refer to an entity by some form of an identifier, you should
always use [string-form entity reference](references.md#string-references)
instead.
### `title` [optional]
A display name of the entity, to be presented in user interfaces instead of the
@@ -1309,3 +1326,7 @@ resolved relative to the location of this Location entity itself.
A list of targets as strings. They can all be either absolute paths/URLs
(depending on the type), or relative paths such as `./details/catalog-info.yaml`
which are resolved relative to the location of this Location entity itself.
### `spec.presence` [optional]
Describes whether the target of a location is required to exist or not. It defaults to `'required'` if not specified, can also be `'optional'`.
@@ -438,7 +438,7 @@ with a new Entity type.
### Creating a custom entity definition
The first step of introducing a custom entity is to define what shape and schema
it has. We do this both using a TypeScript type, along with a JSONSchema schema.
it has. We do this using a TypeScript type, as well as a JSONSchema schema.
Most of the time you will want to have at least the TypeScript type of your
extension available in both frontend and backend code, which means you likely
@@ -474,23 +474,35 @@ entity might look like:
import { CatalogProcessor, processingResult } from '@backstage/catalog-backend';
import { entityKindSchemaValidator } from '@backstage/catalog-model';
// For an example of the JSONSchema format and how to use $ref markers to the
// base definitions, see:
// https://github.com/backstage/backstage/tree/master/packages/catalog-model/src/schema/kinds/Component.v1alpha1.schema.json
import { foobarEntityV1alpha1Schema } from '@internal/catalog-model';
export class FoobarEntitiesProcessor implements CatalogProcessor {
// You often end up wanting to support multiple versions of your kind as you
// iterate on the definition, so we keep each version inside this array.
// iterate on the definition, so we keep each version inside this array as a
// convenient pattern.
private readonly validators = [
// This is where we use the JSONSchema that we export from our isomorphic package
// This is where we use the JSONSchema that we export from our isomorphic
// package
entityKindSchemaValidator(foobarEntityV1alpha1Schema),
];
// validateEntityKind is responsible for signaling to the catalog processing engine
// that this entity is valid and should therefore be submitted for further processing.
// validateEntityKind is responsible for signaling to the catalog processing
// engine that this entity is valid and should therefore be submitted for
// further processing.
async validateEntityKind(entity: Entity): Promise<boolean> {
for (const validator of this.validators) {
// If the validator throws an exception, the entity will be marked as
// invalid.
if (validator(entity)) {
return true;
}
}
// Returning false signals that we don't know what this is, passing the
// responsibility to other processors to try to validate it instead.
return false;
}
@@ -505,8 +517,8 @@ export class FoobarEntitiesProcessor implements CatalogProcessor {
) {
const foobarEntity = entity as FoobarEntityV1alpha1;
// Here we can modify the entity or emit results related to the entity
// Typically you will want to emit any relations associated with the entity here
// Typically you will want to emit any relations associated with the
// entity here.
emit(processingResult.relation({ ... }))
}
@@ -519,8 +531,7 @@ Once the processor is created it can be wired up to the catalog via the
`CatalogBuilder` in `packages/backend/src/plugins/catalog.ts`:
```diff
+ import { FoobarEntitiesProcessor implements CatalogProcessor {
} from '@internal/plugin-foobar-backend';
+ import { FoobarEntitiesProcessor } from '@internal/plugin-foobar-backend';
// ...
@@ -111,10 +111,10 @@ export class FrobsProvider implements EntityProvider {
throw new Error('Not initialized');
}
const raw = await this.reader.read(
const response = await this.reader.readUrl(
`https://frobs-${this.env}.example.com/data`,
);
const data = JSON.parse(raw.toString());
const data = JSON.parse(await response.buffer()).toString();
/** [5] **/
const entities: Entity[] = frobsToEntities(data);
@@ -525,8 +525,8 @@ export class SystemXReaderProcessor implements CatalogProcessor {
// API. If you prefer, you can just use plain fetch here
// (from the node-fetch package), or any other method of
// your choosing.
const data = await this.reader.read(location.target);
const json = JSON.parse(data.toString());
const response = await this.reader.readUrl(location.target);
const json = JSON.parse((await response.buffer()).toString());
// Repeatedly call emit(processingResult.entity(location, <entity>))
} catch (error) {
const message = `Unable to read ${location.type}, ${error}`;
@@ -627,7 +627,7 @@ export class SystemXReaderProcessor implements CatalogProcessor {
// We send the ETag from the previous run if it exists.
// The previous ETag will be set in the headers for the outgoing request and system-x
// is going to throw NOT_MODIFIED (HTTP 304) if the ETag matches.
const response = await this.reader.readUrl?.(location.target, {
const response = await this.reader.readUrl(location.target, {
etag: cacheItem?.etag,
});
if (!response) {
@@ -23,18 +23,19 @@ the `Scaffolder` frontend plugin in your own `App.tsx`.
You can create your own Field Extension by using the
[`createScaffolderFieldExtension`](https://backstage.io/docs/reference/plugin-scaffolder.createscaffolderfieldextension)
`API` like below:
`API` like below.
As an example, we will create a component that validates whether a string is in the `Kebab-case` pattern:
```tsx
//packages/app/src/scaffolder/MyCustomExtension/MyCustomExtension.tsx
//packages/app/src/scaffolder/ValidateKebabCase/ValidateKebabCaseExtension.tsx
import React from 'react';
import { FieldProps, FieldValidation } from '@rjsf/core';
import FormControl from '@material-ui/core/FormControl';
import { KubernetesValidatorFunctions } from '@backstage/catalog-model';
/*
This is the actual component that will get rendered in the form
*/
export const MyCustomExtension = ({
export const ValidateKebabCaseExtension = ({
onChange,
rawErrors,
required,
@@ -45,8 +46,17 @@ export const MyCustomExtension = ({
margin="normal"
required={required}
error={rawErrors?.length > 0 && !formData}
onChange={onChange}
/>
>
<InputLabel htmlFor="validateName">Name</InputLabel>
<Input
id="validateName"
aria-describedby="entityName"
onChange={e => onChange(e.target?.value)}
/>
<FormHelperText id="entityName">
Use only letters, numbers, hyphens and underscores
</FormHelperText>
</FormControl>
);
};
@@ -55,20 +65,22 @@ export const MyCustomExtension = ({
You will get the value from the `onChange` handler before as the value here to make sure that the types are aligned\
*/
export const myCustomValidation = (
export const validateKebabCaseValidation = (
value: string,
validation: FieldValidation,
) => {
if (!KubernetesValidatorFunctions.isValidObjectName(value)) {
const kebabCase = /^[a-z0-9-_]+$/g.test(value);
if (kebabCase === false) {
validation.addError(
'must start and end with an alphanumeric character, and contain only alphanumeric characters, hyphens, underscores, and periods. Maximum length is 63 characters.',
`Only use letters, numbers, hyphen ("-") and underscore ("_").`,
);
}
};
```
```tsx
// packages/app/src/scaffolder/MyCustomExtension/extensions.ts
// packages/app/src/scaffolder/ValidateKebabCase/extensions.ts
/*
This is where the magic happens and creates the custom field extension.
@@ -81,21 +93,24 @@ import {
scaffolderPlugin,
createScaffolderFieldExtension,
} from '@backstage/plugin-scaffolder';
import { MyCustomExtension, myCustomValidation } from './MyCustomExtension';
import {
ValidateKebabCase,
validateKebabCaseValidation,
} from './ValidateKebabCase';
export const MyCustomFieldExtension = scaffolderPlugin.provide(
export const ValidateKebabCaseFieldExtension = scaffolderPlugin.provide(
createScaffolderFieldExtension({
name: 'MyCustomExtension',
component: MyCustomExtension,
validation: myCustomValidation,
name: 'ValidateKebabCase',
component: ValidateKebabCase,
validation: validateKebabCaseValidation,
}),
);
```
```tsx
// packages/app/src/scaffolder/MyCustomExtension/index.ts
// packages/app/src/scaffolder/ValidateKebabCase/index.ts
export { MyCustomFieldExtension } from './extensions';
export { ValidateKebabCaseFieldExtension } from './extensions';
```
Once all these files are in place, you then need to provide your custom
@@ -117,7 +132,7 @@ const routes = (
Should look something like this instead:
```tsx
import { MyCustomFieldExtension } from './scaffolder/MyCustomExtension';
import { ValidateKebabCaseFieldExtension } from './scaffolder/ValidateKebabCase';
import { ScaffolderFieldExtensions } from '@backstage/plugin-scaffolder';
const routes = (
@@ -125,7 +140,7 @@ const routes = (
...
<Route path="/create" element={<ScaffolderPage />}>
<ScaffolderFieldExtensions>
<MyCustomFieldExtension />
<ValidateKebabCaseFieldExtension />
</ScaffolderFieldExtensions>
</Route>
...
@@ -158,7 +173,9 @@ spec:
title: Name
type: string
description: My custom name for the component
ui:field: MyCustomExtension
ui:field: ValidateKebabCaseExtension
steps:
[...]
```
## Access Data from other Fields
@@ -44,8 +44,8 @@ spec:
description: Owner of the component
ui:field: OwnerPicker
ui:options:
allowedKinds:
- Group
catalogFilter:
kind: Group
- title: Choose a location
required:
- repoUrl
@@ -165,11 +165,11 @@ this:
"firstName": {
"ui:autofocus": true,
"ui:emptyValue": "",
"ui:autocomplete": "family-name"
"ui:autocomplete": "given-name"
},
"lastName": {
"ui:emptyValue": "",
"ui:autocomplete": "given-name"
"ui:autocomplete": "family-name"
},
"nicknames": {
"ui:options":{
@@ -211,12 +211,12 @@ spec:
default: Chuck
ui:autofocus: true
ui:emptyValue: ''
ui:autocomplete: family-name
ui:autocomplete: given-name
lastName:
type: string
title: Last name
ui:emptyValue: ''
ui:autocomplete: given-name
ui:autocomplete: family-name
nicknames:
type: array
items:
@@ -459,7 +459,7 @@ an owner for them. Ideally, users should be able to select an owner when they go
through the scaffolder form from the users and groups already known to
Backstage. The `OwnerPicker` is a custom field that generates a searchable list
of groups and/or users already in the catalog to pick an owner from. You can
specify which of the two kinds are listed in the `allowedKinds` option:
specify which of the two kinds (or both) are listed in the `catalogFilter.kind` option:
```yaml
owner:
@@ -468,8 +468,8 @@ owner:
description: Owner of the component
ui:field: OwnerPicker
ui:options:
allowedKinds:
- Group
catalogFilter:
kind: [Group, User]
```
## `spec.steps` - `Action[]`
+16 -8
View File
@@ -70,6 +70,11 @@ a Backstage app server on port 3000. The Backstage app has a custom TechDocs API
implementation, which uses the MkDocs preview server as a proxy to fetch the
generated documentation files and assets.
Backstage instances might differ from the provided preview app in appearance and
behavior. To preview documentation with a different app, use
`--preview-app-bundle-path` with a path to the bundle of the app to use instead.
Typically, a `dist` or `build` directory.
NOTE: When using a custom `techdocs` docker image, make sure the entry point is
also `ENTRYPOINT ["mkdocs"]` or override with `--docker-entrypoint`.
@@ -81,14 +86,17 @@ Usage: techdocs-cli serve [options]
Serve a documentation project locally in a Backstage app-like environment
Options:
-i, --docker-image <DOCKER_IMAGE> The mkdocs docker container to use (default: "spotify/techdocs")
--docker-entrypoint <DOCKER_ENTRYPOINT> Override the image entrypoint
--docker-option <DOCKER_OPTION...> Extra options to pass to the docker run command, e.g. "--add-host=internal.host:192.168.11.12"
(can be added multiple times).
--no-docker Do not use Docker, use MkDocs executable in current user environment.
--mkdocs-port <PORT> Port for MkDocs server to use (default: "8000")
-v --verbose Enable verbose output. (default: false)
-h, --help display help for command
-i, --docker-image <DOCKER_IMAGE> The mkdocs docker container to use (default: "spotify/techdocs")
--docker-entrypoint <DOCKER_ENTRYPOINT> Override the image entrypoint
--docker-option <DOCKER_OPTION...> Extra options to pass to the docker run command, e.g. "--add-host=internal.host:192.168.11.12"
(can be added multiple times).
--no-docker Do not use Docker, use MkDocs executable in current user environment.
--mkdocs-port <PORT> Port for MkDocs server to use (default: "8000")
--preview-app-bundle-path <PATH_TO_BUNDLE> Preview documentation using a web app other than the included one.
--preview-app-port <PORT> Port where the preview will be served.
Can only be used with "--preview-app-bundle-path". (default: "3000")
-v --verbose Enable verbose output. (default: false)
-h, --help display help for command
```
### Generate TechDocs site from a documentation project
+1 -1
View File
@@ -116,7 +116,7 @@ yarn clean # Remove all output folders and @backstage/cli cache
yarn diff # Make sure all plugins are up to date with the latest plugin template
yarn create-plugin # Create a new plugin
yarn new # Create a new module
```
> See
+1 -1
View File
@@ -49,7 +49,7 @@ installation. Running the command below will install Backstage. The wizard will
create a subdirectory inside your current working directory.
```bash
npx @backstage/create-app
npx @backstage/create-app@latest
```
The wizard will ask you for the name of the app, which will also be the name of the directory
@@ -47,7 +47,7 @@ For this reason, any changes made to the template are documented along with
upgrade instructions in the
[changelog](https://github.com/backstage/backstage/blob/master/packages/create-app/CHANGELOG.md)
of the `@backstage/create-app` package. We recommend peeking at this changelog
-for any applicable updates when upgrading packages. As an alternative, the
for any applicable updates when upgrading packages. As an alternative, the
[Backstage Upgrade Helper](https://backstage.github.io/upgrade-helper/) provides
a consolidated view of all the changes between two versions of Backstage. You
can find the current version of your Backstage installation in `backstage.json`.
+1 -1
View File
@@ -101,7 +101,7 @@ are separated out into their own folder, see further down.
- [`cli/`](https://github.com/backstage/backstage/tree/master/packages/cli) -
One of the biggest packages in our project, the `cli` is used to build, serve,
diff, create-plugins and more. In the early days of this project, we started
diff, create plugins and more. In the early days of this project, we started
out with calling tools directly - such as `eslint` - through `package.json`.
But as it was tricky to have a good development experience around that when we
change named tooling, we opted for wrapping those in our own CLI. That way
+47 -1
View File
@@ -17,7 +17,7 @@ entities that mirror your org setup.
> provide authentication. See the
> [GitHub auth provider](../../auth/github/provider.md) for that.
## Installation
## Installation without Events Support
This guide will use the Entity Provider method. If you for some reason prefer
the Processor method (not recommended), it is described separately below.
@@ -60,6 +60,52 @@ schedule it:
+ );
```
## Installation with Events Support
Please follow the installation instructions at
- https://github.com/backstage/backstage/tree/master/plugins/events-backend/README.md
- https://github.com/backstage/backstage/tree/master/plugins/events-backend-module-github/README.md
Additionally, you need to decide how you want to receive events from external sources like
- [via HTTP endpoint](https://github.com/backstage/backstage/tree/master/plugins/events-backend/README.md)
- [via an AWS SQS queue](https://github.com/backstage/backstage/tree/master/plugins/events-backend-module-aws-sqs/README.md)
Set up your provider
```diff
// packages/backend/src/plugins/catalogEventBasedProviders.ts
+import { GithubOrgEntityProvider } from '@backstage/plugin-catalog-backend-module-github';
import { EntityProvider } from '@backstage/plugin-catalog-node';
import { EventSubscriber } from '@backstage/plugin-events-node';
import { PluginEnvironment } from '../types';
export default async function createCatalogEventBasedProviders(
- _: PluginEnvironment,
+ env: PluginEnvironment,
): Promise<Array<EntityProvider & EventSubscriber>> {
const providers: Array<
(EntityProvider & EventSubscriber) | Array<EntityProvider & EventSubscriber>
> = [];
- // add your event-based entity providers here
+ providers.push(
+ GithubOrgEntityProvider.fromConfig(env.config, {
+ id: 'production',
+ orgUrl: 'https://github.com/backstage',
+ logger: env.logger,
+ schedule: env.scheduler.createScheduledTaskRunner({
+ frequency: { minutes: 60 },
+ timeout: { minutes: 15 },
+ }),
+ }),
+ );
return providers.flat();
}
```
You can check the official docs to [configure your webhook](https://docs.github.com/en/developers/webhooks-and-events/webhooks/creating-webhooks) and to [secure your request](https://docs.github.com/en/developers/webhooks-and-events/webhooks/securing-your-webhooks).
The webhook will need to be configured to forward `organization`,`team` and `membership` events.
## Configuration
As mentioned above, you also must have some configuration in your app-config
+4 -4
View File
@@ -201,15 +201,15 @@ Usage: backstage-cli package postpack [options]
Restores the changes made by the prepack command
```
## create
## new
The `create` command opens up an interactive guide for you to create new things
The `new` command opens up an interactive guide for you to create new things
in your app. If you do not pass in any options it is completely interactive, but
it is possible to pre-select what you want to create using the `--select` flag,
and provide options using `--options`, for example:
and provide options using `--option`, for example:
```bash
backstage-cli create --select plugin --option id=foo
backstage-cli new --select plugin --option id=foo
```
This command is typically added as script in the root `package.json` to be
+1 -1
View File
@@ -54,7 +54,7 @@ Backstage also supports authentication through a proxy where the user identity i
## Catalog
Integrators should configure `catalog.rules` and `catalog.locations[].rules` to limit the allowed entity kinds that users can define. In general it is best to restrict definition of User, Group, and Template entities so that internal users cannot register additional ones. Template entities define actions that are executed on the backend hosts, and while the goal is for these actions to be secure regardless of input, it is still a more sensitive context and it is recommended that you protect it with additional checks. It is very important to not allow registration of User and Group entities if you ingest and rely on these as organizational data in your catalog. Doing so could otherwise open up for the ability to impersonate users and confuse group membership information. You should always ingest organizational data using a statically configured catalog location or an entity provider reading from a trusted source. The entities emitted directly by an entity provider are always trusted and rules are not applied to them, but any entities produced further down the chain are still subject to the rules.
Integrators should configure [catalog rules](https://backstage.io/docs/features/software-catalog/configuration#catalog-rules) to limit the allowed entity kinds that users can define. In general it is best to restrict definition of User, Group, and Template entities so that internal users cannot register additional ones. Template entities define actions that are executed on the backend hosts, and while the goal is for these actions to be secure regardless of input, it is still a more sensitive context and it is recommended that you protect it with additional checks. It is very important to not allow registration of User and Group entities if you ingest and rely on these as organizational data in your catalog. Doing so could otherwise open up for the ability to impersonate users and confuse group membership information. You should always ingest organizational data using a statically configured catalog location or an entity provider reading from a trusted source. The entities emitted directly by an entity provider are always trusted and rules are not applied to them, but any entities produced further down the chain are still subject to the rules.
The Catalog does not aim to protect against resource exhaustion attacks in its default setup. If you need to prevent your internal users from being able to register large amounts of entities, then it is recommended to disable entity registration and use a different approach for discovering entities. One way to mitigate any resource exhaustion attacks is to only allow the catalog to read from trusted SCM sources that have an audit trail. Catalog currently lacks limits for entity hierarchy depth and entity size, which we hope to address in the future.
+2 -3
View File
@@ -116,9 +116,8 @@ For packages at version `1.0.0` or above, the following policy also applies:
before it can be removed.
- The release of breaking changes document a clear upgrade path in the
changelog, both when deprecations are introduced and when they are removed.
- Exports that have been marked as `@alpha` or `@beta` may receive breaking
changes without a deprecation period, but the changes must still adhere to
semver.
- Breaking changes to `@alpha` or `@beta` exports must result in at least a minor
version bump, and may be done without a deprecation period.
### Changes that are Not Considered Breaking
+1 -1
View File
@@ -28,7 +28,7 @@ lowercase characters separated by dashes, for example `carmen`, if it's a
package that adds an integration with a system named Carmen, for example. The
full NPM package name would then be something like
`@internal/plugin-carmen-backend`, depending on the other flags passed to the
`create-plugin` command, and your settings for the `create-plugin` command in
`new` command, and your settings for the `new` command in
your root `package.json`.
Creating the plugin will take a little while, so be patient. It will helpfully
+1 -1
View File
@@ -11,7 +11,7 @@ A Backstage Plugin adds functionality to Backstage.
To create a new plugin, make sure you've run `yarn install` and installed
dependencies, then run the following on your command line (a shortcut to
invoking the
[`backstage-cli create-plugin`](../local-dev/cli-commands.md#create-plugin))
[`backstage-cli new --select plugin`](../local-dev/cli-commands.md#new))
from the root of your project.
```bash
+21 -4
View File
@@ -1,16 +1,18 @@
---
id: feature-flags
title: Feature Flags
description: Details the process of defining setting and reading a plugin feature flag.
description: Details the process of defining setting and reading a feature flag.
---
Backstage offers the ability to define feature flags inside a plugin. This allows you to restrict parts of your plugin to those individual users who have toggled the feature flag to on.
Backstage offers the ability to define feature flags inside a plugin or during application creation. This allows you to restrict parts of your plugin to those individual users who have toggled the feature flag to on.
This page describes the process of defining setting and reading a plugin feature flag. If you are looking for using feature flags with software templates that can be found under [Writing Templates](https://backstage.io/docs/features/software-templates/writing-templates#remove-sections-or-fields-based-on-feature-flags).
This page describes the process of defining setting and reading a feature flag. If you are looking for using feature flags with software templates that can be found under [Writing Templates](https://backstage.io/docs/features/software-templates/writing-templates#remove-sections-or-fields-based-on-feature-flags).
## Defining a Feature Flag
Before using a feature flag we must first define it. This is done when we create the plugin by passing the name of the feature flag into the `featureFlags` array.
### In a plugin
Defining feature flag in a plugin is done by passing the name of the feature flag into the `featureFlags` array:
```ts
/* src/plugin.ts */
@@ -26,6 +28,21 @@ export const examplePlugin = createPlugin({
});
```
### In the application
Defining feature flag in the application is done by adding feature flags in`featureFlags` array in
`createApp()` function call:
```ts
const app = createApp({
// ...
featureFlags: [
{ name: 'tech-radar', description: 'Enables the tech radar plugin' },
],
// ...
});
```
## Enabling Feature Flags
Feature flags are defaulted to off and can be updated by individual users in the backstage interface.
@@ -21,7 +21,7 @@ should have a separate package in a folder, which represents your plugin.
Example:
```
$ yarn create-plugin
$ yarn new --select plugin
> ? Enter an ID for the plugin [required] my-plugin
> ? Enter the owner(s) of the plugin. If specified, this will be added to CODEOWNERS for the plugin path. [optional]
@@ -333,3 +333,85 @@ reading the search context.
If you produce something generic and reusable, consider contributing your
component upstream so that all users of the Backstage Search Platform can
benefit. Issues and pull requests welcome.
#### Custom search results
Search results throughout Backstage are rendered as lists so that list items can easily be customized; although a [default result list item](https://backstage.io/storybook/?path=/story/plugins-search-defaultresultlistitem--default) is available, plugins are in the best position to provide custom result list items that surface relevant information only known to the plugin.
The example below imagines `YourCustomSearchResult` as a type of search result that contains associated `tags` which could be rendered as chips below the title/text.
```tsx
import { Link } from '@backstage/core-components';
import { useAnalytics } from '@backstage/core-plugin-api';
import { ResultHighlight } from '@backstage/plugin-search-common';
import { HighlightedSearchResultText } from '@backstage/plugin-search-react';
type CustomSearchResultListItemProps = {
result: YourCustomSearchResult;
rank?: number;
highlight?: ResultHighlight;
};
export const CustomSearchResultListItem = (
props: CustomSearchResultListItemProps,
) => {
const { title, text, location, tags } = props.result;
const analytics = useAnalytics();
const handleClick = () => {
analytics.captureEvent('discover', title, {
attributes: { to: location },
value: props.rank,
});
};
return (
<Link noTrack to={location} onClick={handleClick}>
<ListItem alignItems="center">
<Box flexWrap="wrap">
<ListItemText
primaryTypographyProps={{ variant: 'h6' }}
primary={
highlight?.fields?.title ? (
<HighlightedSearchResultText
text={highlight.fields.title}
preTag={highlight.preTag}
postTag={highlight.postTag}
/>
) : (
title
)
}
secondary={
highlight?.fields?.text ? (
<HighlightedSearchResultText
text={highlight.fields.text}
preTag={highlight.preTag}
postTag={highlight.postTag}
/>
) : (
text
)
}
/>
{tags &&
tags.map((tag: string) => (
<Chip key={tag} label={`Tag: ${tag}`} size="small" />
))}
</Box>
</ListItem>
<Divider />
</Link>
);
};
```
The optional use of the `<HighlightedSearchResultText>` component makes it possible to highlight relevant parts of the result based on the user's search query.
**Note on Analytics**: In order for app integrators to track and improve search experiences across Backstage, it's important for them to understand when and what users search for, as well as what they click on after searching. When providing a custom result component, it's your responsibility as a plugin developer to instrument it according to search analytics conventions. In particular:
- You must use the `analytics.captureEvent` method, from the `useAnalytics()` hook (detailed [plugin analytics docs are here](./analytics.md)).
- You must ensure that the action of the event, representing a click on a search result item, is `discover`, and the subject is the `title` of the clicked result. In addition, the `to` attribute should be set to the result's `location`, and the `value` of the event must be set to the `rank` (passed in as a prop).
- You must ensure that the aforementioned `captureEvent` method is called when a user clicks the link; you should further ensure that the `noTrack` prop is added to the link (which disables default link click tracking, in favor of this custom instrumentation).
For other examples and inspiration on custom result list items, check out the [`<StackOverflowSearchResultListItem>`](https://github.com/backstage/backstage/blob/c981e83/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.tsx) or [`<CatalogSearchResultListItem>`](https://github.com/backstage/backstage/blob/c981e83/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx) components.
+23 -23
View File
@@ -8,13 +8,13 @@ description: Details of the upcoming backend system
## Status
The new backend system is under active development, and only a small number of plugins and services have been migrated so far. It is possible to try it out, but it is not recommended to use this new system in production yet.
The new backend system is under active development, and only a small number of plugins have been migrated so far. It is possible to try it out, but it is not recommended to use this new system in production yet.
You can find an example backend setup at https://github.com/backstage/backstage/tree/master/packages/backend-next.
You can find an example backend setup in [the backend-next package](https://github.com/backstage/backstage/tree/master/packages/backend-next).
## Overview
The new Backstage backend system is being built to help make it simpler to install backend plugins and keep projects up to date. It also changes the foundation to one that makes it a lot easier to evolve plugins and the system itself. You can read more about the reasoning in the [original RFC](https://github.com/backstage/backstage/issues/11611).
The new Backstage backend system is being built to help make it simpler to install backend plugins and to keep projects up to date. It also changes the foundation to one that makes it a lot easier to evolve plugins and the system itself with minimal disruption or cause for breaking changes. You can read more about the reasoning in the [original RFC](https://github.com/backstage/backstage/issues/11611).
One of the goals of the new system was to reduce the code needed for setting up a Backstage backend and installing plugins. This is an example of how you create, add features, and start up your backend in the new system:
@@ -32,21 +32,21 @@ backend.add(catalogPlugin());
await backend.start();
```
One notable change that helped achieve this much slimmer backend setup is the introduction of dependency injection, with a system that is very similar to the one in the Backstage frontend.
One notable change that helped achieve this much slimmer backend setup is the introduction of a system for dependency injection, which is very similar to the one in the Backstage frontend.
## Building Blocks
This section introduces the high-level building blocks upon which this new system is built. These are all concepts that exist in our current system in one way or another, but the have all been lifted up to be first class concerns in the new system.
This section introduces the high-level building blocks upon which this new system is built. These are all concepts that exist in our current system in one way or another, but they have all been lifted up to be first class concerns in the new system.
### Backend
This is the backend instance itself, which you can think of as the unit of deployment. It does not have any functionality in itself, but is simply responsible for wiring things together.
This is the backend instance itself, which you can think of as the unit of deployment. It does not have any functionality in and of itself, but is simply responsible for wiring things together.
It is up to you to decide how many different backends you want to deploy. You can have all features in a single one, or split things out into multiple smaller deployments. All depending on your need to scale and isolate individual features.
### Plugins
Plugins provide the actual features, just like in our existing system. They operate completely independently of each other. If plugins what to communicate with each other, they must do so over the wire. There can be no direct communication between plugins through code. Because of this constraints, each plugins can be considered to be its own microservice.
Plugins provide the actual features, just like in our existing system. They operate completely independently of each other. If plugins want to communicate with each other, they must do so over the wire. There can be no direct communication between plugins through code. Because of this constraint, each plugin can be considered to be its own microservice.
### Services
@@ -77,6 +77,7 @@ Plugins are created using the `createBackendPlugin` function. All plugins must h
```ts
import {
configServiceRef,
coreServices,
createBackendPlugin,
} from '@backstage/backend-plugin-api';
@@ -89,7 +90,7 @@ export const examplePlugin = createBackendPlugin({
register(env) {
env.registerInit({
deps: {
logger: loggerServiceRef,
logger: coreServices.logger,
},
// logger is provided by the backend based on the dependency on loggerServiceRef above.
async init({ logger }) {
@@ -113,7 +114,7 @@ export const examplePlugin = createBackendPlugin({
id: 'example',
register(env, options?: { silent?: boolean }) {
env.registerInit({
deps: { logger: loggerServiceRef },
deps: { logger: coreServices.logger },
async init({ logger }) {
if (!options?.silent) {
logger.info('Hello from example plugin');
@@ -188,7 +189,7 @@ Extension points are registered by a plugin and extended by modules.
## Backend Services
The default backend provides several _services_ out of the box which includes access to configuration, logging, databases and more.
The default backend provides several [core services](https://github.com/backstage/backstage/blob/master/packages/backend-plugin-api/src/services/definitions/coreServices.ts) out of the box which includes access to configuration, logging, databases and more.
Service dependencies are declared using their `ServiceRef`s in the `deps` section of the plugin or module, and the implementations are then forwarded to the `init` method of the plugin or module.
### Service References
@@ -204,8 +205,7 @@ ServiceRefs contain a scope which is used to determine if the serviceFactory cre
```ts
import {
createServiceFactory,
pluginMetadataServiceRef,
loggerServiceRef,
coreServices,
} from '@backstage/backend-plugin-api';
import { ExampleImpl } from './ExampleImpl';
@@ -223,11 +223,11 @@ export const exampleServiceRef = createServiceRef<ExampleApi>({
createServiceFactory({
service,
deps: {
logger: loggerServiceRef,
plugin: pluginMetadataServiceRef,
logger: coreServices.logger,
plugin: coreServices.pluginMetadata,
},
// Logger is available directly in the factory as it's a root scoped service and will be created once per backend instance.
async factory({ logger }) {
async factory({ logger, plugin }) {
// plugin is available as it's a plugin scoped service and will be created once per plugin.
return async ({ plugin }) => {
// This block will be executed once for every plugin that depends on this service
@@ -277,20 +277,20 @@ const backend = createBackend({
## Testing
Utilities for testing backend plugins and modules are available in `@backstage/backend-test-utils`.
`startTestBackend` returns the HTTP which can be used together with `supertest` to test the plugin.
```ts
import { startTestBackend } from '@backstage/backend-test-utils';
import request from 'supertest';
describe('Example', () => {
it('should do something', async () => {
await startTestBackend({
// mock services can be provided to the backend
services: [someServiceFactory],
// plugins and modules for testing
features: [testModule()],
describe('My plugin tests', () => {
it('should return 200', async () => {
const { server } = await startTestBackend({
features: [myPlugin()],
});
// assertions
const response = await request(server).get('/api/example/hello');
expect(response.status).toBe(200);
});
});
```
+7 -25
View File
@@ -60,15 +60,7 @@ The generic interface of a URL Reader instance looks like this.
```ts
export type UrlReader = {
/* Used to read a single file and return its content. */
read(url: string): Promise<Buffer>;
/**
* A replacement for the read method that supports options and complex responses.
*
* Use this whenever it is available, as the read method will be deprecated and
* eventually removed in the future.
*/
readUrl?(url: string, options?: ReadUrlOptions): Promise<ReadUrlResponse>;
readUrl(url: string, options?: ReadUrlOptions): Promise<ReadUrlResponse>;
/* Used to read a file tree and download as a directory. */
readTree(url: string, options?: ReadTreeOptions): Promise<ReadTreeResponse>;
/* Used to search a file in a tree. */
@@ -102,8 +94,8 @@ backend plugins.
Once the reader instance is available inside the plugin, one of its methods can
directly be used with a URL. Some example usages -
- [`read`](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/plugins/catalog-backend/src/ingestion/processors/codeowners/read.ts#L24-L33) -
Catalog using the `read` method to read the CODEOWNERS file in a repository.
- [`readUrl`](https://github.com/backstage/backstage/blob/a7607b5/plugins/catalog-backend/src/modules/codeowners/lib/read.ts#L24-L33) -
Catalog using the `readUrl` method to read the CODEOWNERS file in a repository.
- [`readTree`](https://github.com/backstage/backstage/blob/84a8788/plugins/techdocs-node/src/helpers.ts#L146-L167) -
TechDocs using the `readTree` method to download markdown files in order to
generate the documentation site.
@@ -155,11 +147,9 @@ all the methods of the `UrlReader` interface should be implemented. However it
is okay to start by implementing just one of them and create issues for the
remaining.
#### read
#### `readUrl`
NOTE: Use `readUrl` instead of `read`.
`read` method expects a user-friendly URL, something which can be copied from
`readUrl` method expects a user-friendly URL, something which can be copied from
the browser naturally when a person is browsing the provider in their browser.
- ✅ Valid URL :
@@ -168,18 +158,10 @@ the browser naturally when a person is browsing the provider in their browser.
`https://raw.githubusercontent.com/backstage/backstage/master/ADOPTERS.md`
- ❌ Not a valid URL : `https://github.com/backstage/backstage/ADOPTERS.md`
Upon receiving the URL, `read` converts the user-friendly URL into an API URL
Upon receiving the URL, `readUrl` converts the user-friendly URL into an API URL
which can be used to request the provider's API.
`read` then makes an authenticated request to the provider API and returns the
file's content.
#### `readUrl`
`readUrl` is a new interface that allows complex response objects and is
intended to replace the `read` method. This new method is currently optional to
implement which allows for a soft migration to `readUrl` instead of `read` in
the future.
`readUrl` then makes an authenticated request to the provider API and returns the response containing the file's contents and ETag(if the provider supports it).
#### `readTree`
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+78
View File
@@ -0,0 +1,78 @@
---
id: v1.10.0
title: v1.10.0
description: Backstage Release v1.10.0
---
These are the release notes for the v1.10.0 release of [Backstage](https://backstage.io/).
A huge thanks to the whole team of maintainers and contributors as well as the amazing Backstage Community for the hard work in getting this release developed and done.
## Highlights
### `yarn new` instead of `yarn create-plugin`
The `create-plugin` command of the `backstage-cli` is deprecated, and we have removed its usage in newly created apps and in our documentation. We recommend that you update your root `package.json` as follows:
```diff
"scripts": {
...
- "create-plugin": "backstage-cli create-plugin --scope internal"
+ "new": "backstage-cli new --scope internal"
}
```
### New Package: `@backstage/plugin-scaffolder-react`
Weve made it easier to re-use some of the Scaffolder components outside of the Scaffolder plugin as well as re-homing some common types that might live between other frontend modules in the future. With this change theres been some deprecations that have been shipped which are just renaming the imports from `@backstage/plugin-scaffolder` to `@backstage/plugin-scaffolder-react`. You can find out more in the [changelog](https://github.com/backstage/backstage/blob/master/plugins/scaffolder/CHANGELOG.md#minor-changes) for the `@backstage/plugin-scaffolder`
### New Package: `@backstage/plugin-scaffolder-backend-module-sentry`
A new module has been created that allows the creation of [Sentry](https://sentry.io) projects with a new `sentry:project:create` action.
Contributed by [@dpfaffenbauer](https://github.com/dpfaffenbauer) in [#15639](https://github.com/backstage/backstage/pull/15639)
### New Package: `@backstage/repo-tools`
Parts of the tooling that powers the Backstage main repository are being externalized, into [the `@backstage/repo-tools` package](https://www.npmjs.com/package/@backstage/repo-tools). Documentation is sparse and so far it houses only the API extractor (which makes the `api-report.md` files) and the type dependencies check which makes sure that `@types/*` packages that are visible in the public API are added as dependencies.
### Scaffolder Improvements
Examples can now be added to `createTemplateAction` in the backend and can be browsed under `/create/actions` on your Backstage instance.
Contributed by [@punkle](https://github.com/punkle) in [#15341](https://github.com/backstage/backstage/pull/15341)
The `allowedKinds` has been deprecated from the `OwnerPicker` and `EntityPicker` in favor of a `catalogFilter` option that allows you to be more flexible in the filtering from the Catalog.
Contributed by [@rikez](https://github.com/rikez) in [#15063](https://github.com/backstage/backstage/pull/15063)
### Backend System Progress
Weve made good progress towards getting the new backend system ready for broader testing. While were not quite there yet, youll see a lot of changes in the `@backstage/backend-app-api` and `@backstage/backend-plugin-api` packages as well as supporting packages like `@backstage/backend-defaults`.
### Event based updates in `GithubOrgEntityProvider`
Theres some initial support for event (web hook) based updates for GitHub org entities that are ingested using `GithubOrgEntityProvider`. We are excited to see the `events-backend` starting to power this type of functionality. More information on the setup can be found [in the documentation](https://backstage.io/docs/integrations/github/org#installation-with-events-support).
Contributed by [@angeliski](https://github.com/angeliski) in [#14870](https://github.com/backstage/backstage/pull/14870)
## Security Fixes
This release does not contain any security fixes.
## Upgrade path
We recommend that you keep your Backstage project up to date with this latest release. For more guidance on how to upgrade, check out the documentation for [keeping Backstage updated](https://backstage.io/docs/getting-started/keeping-backstage-updated).
## Links and References
Below you can find a list of links and references to help you learn about and start using this new release.
- [Backstage official website](https://backstage.io/), [documentation](https://backstage.io/docs/), and [getting started guide](https://backstage.io/docs/getting-started/)
- [GitHub repository](https://github.com/backstage/backstage)
- Backstage's [versioning and support policy](https://backstage.io/docs/overview/versioning-policy)
- [Community Discord](https://discord.gg/bFESRKVt) for discussions and support
- [Changelog](https://github.com/backstage/backstage/tree/master/docs/releases/v1.10.0-changelog.md)
- Backstage [Demos](https://backstage.io/demos), [Blog](https://backstage.io/blog), [Roadmap](https://backstage.io/docs/overview/roadmap) and [Plugins](https://backstage.io/plugins)
Sign up for our [newsletter](https://mailchi.mp/spotify/backstage-community) if you want to be informed about what is happening in the world of Backstage.
+1 -1
View File
@@ -28,7 +28,7 @@ organization to easily install and add to their Backstage installation.
# 1. A New Plugin
Sam chooses to develop this plugin in a standalone project and creates a new
plugin using `npx @backstage/cli create-plugin`, which detects that it's not
plugin using `npx @backstage/cli new --select plugin`, which detects that it's not
being run in an existing project and therefore creates a separate plugin repo.
Spinning up the frontend with `yarn start`, Sam goes to work with getting the
+2 -2
View File
@@ -29,8 +29,8 @@ title: Adding Custom Plugin to Existing Monorepo App
# The Skeleton Plugin
1. Start by using the built in creator. From the terminal and root of your
project run: `yarn create-plugin`
1. Start by using the built-in creator. From the terminal and root of your
project run: `yarn new --select plugin`
1. Enter a plugin ID. I used `github-playground`
1. When the process finishes, let's start the backend:
`yarn --cwd packages/backend start`
+4 -4
View File
@@ -4,9 +4,8 @@
"license": "Apache-2.0",
"private": true,
"scripts": {
"examples": "docusaurus-examples",
"start": "docusaurus start",
"build": "docusaurus build",
"start": "node scripts/pre-build.js && docusaurus start",
"build": "node scripts/pre-build.js && docusaurus build",
"prettier:check": "prettier --check .",
"publish-gh-pages": "docusaurus-publish",
"write-translations": "docusaurus-write-translations",
@@ -20,7 +19,8 @@
"devDependencies": {
"@spotify/prettier-config": "^14.0.0",
"js-yaml": "^4.1.0",
"prettier": "^2.6.2"
"prettier": "^2.6.2",
"replace": "^1.2.2"
},
"prettier": "@spotify/prettier-config",
"dependencies": {
+49
View File
@@ -0,0 +1,49 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const replace = require('replace');
const { existsSync, writeFileSync, mkdirSync } = require('fs');
const path = require('path');
const PLACEHOLDER = `---
id: "index"
title: "Package Index"
description: "Index of all Backstage Packages"
---
Run \`yarn build:api-docs\` to generate the API docs.
`;
async function main() {
const referencesDir = '../docs/reference';
if (existsSync(referencesDir)) {
console.log('Removing HTML comments from docs/reference folder');
replace({
regex: '<!--(.*?)-->',
replacement: '',
paths: [referencesDir],
recursive: true,
silent: false,
});
} else {
mkdirSync(referencesDir);
writeFileSync(path.join(referencesDir, 'index.md'), PLACEHOLDER);
}
}
main().catch(error => {
console.error(error.stack);
process.exit(1);
});
+366 -1
View File
@@ -1 +1,366 @@
{}
{
"releases": {
"Release Notes": [
"releases/v1.8.0",
"releases/v1.7.0",
"releases/v1.6.0",
"releases/v1.5.0",
"releases/v1.4.0",
"releases/v1.3.0",
"releases/v1.2.0",
"releases/v1.1.0",
"releases/v1.0.0"
]
},
"docs": {
"Overview": [
"overview/what-is-backstage",
"overview/architecture-overview",
"overview/roadmap",
"overview/vision",
"overview/background",
"overview/adopting",
"overview/versioning-policy",
"overview/threat-model",
"overview/support",
"overview/glossary",
"overview/logos"
],
"Getting Started": [
"getting-started/index",
"getting-started/configuration",
"getting-started/create-an-app",
"getting-started/running-backstage-locally",
{
"type": "category",
"label": "App configuration",
"items": [
"getting-started/configure-app-with-plugins",
"getting-started/app-custom-theme",
"getting-started/homepage"
]
},
"getting-started/keeping-backstage-updated",
"getting-started/concepts",
"getting-started/contributors",
"getting-started/project-structure"
],
"Local Development": [
{
"type": "category",
"label": "CLI",
"items": [
"local-dev/cli-overview",
"local-dev/cli-build-system",
"local-dev/cli-commands"
]
},
"local-dev/linking-local-packages"
],
"Core Features": [
{
"type": "category",
"label": "Software Catalog",
"items": [
"features/software-catalog/software-catalog-overview",
"features/software-catalog/life-of-an-entity",
"features/software-catalog/configuration",
"features/software-catalog/system-model",
"features/software-catalog/descriptor-format",
"features/software-catalog/references",
"features/software-catalog/well-known-annotations",
"features/software-catalog/well-known-relations",
"features/software-catalog/well-known-statuses",
"features/software-catalog/extending-the-model",
"features/software-catalog/external-integrations",
"features/software-catalog/catalog-customization",
"features/software-catalog/software-catalog-api"
]
},
{
"type": "category",
"label": "Kubernetes",
"items": [
"features/kubernetes/overview",
"features/kubernetes/installation",
"features/kubernetes/configuration",
"features/kubernetes/authentication",
"features/kubernetes/troubleshooting"
]
},
{
"type": "category",
"label": "Software Templates",
"items": [
"features/software-templates/software-templates-index",
"features/software-templates/configuration",
"features/software-templates/adding-templates",
"features/software-templates/writing-templates",
"features/software-templates/input-examples",
"features/software-templates/builtin-actions",
"features/software-templates/writing-custom-actions",
"features/software-templates/writing-custom-field-extensions",
"features/software-templates/writing-custom-step-layouts",
"features/software-templates/migrating-from-v1beta2-to-v1beta3"
]
},
{
"type": "category",
"label": "Backstage Search",
"items": [
"features/search/search-overview",
"features/search/getting-started",
"features/search/concepts",
"features/search/architecture",
"features/search/search-engines",
"features/search/how-to-guides"
]
},
{
"type": "category",
"label": "TechDocs",
"items": [
"features/techdocs/techdocs-overview",
"features/techdocs/getting-started",
"features/techdocs/concepts",
"features/techdocs/addons",
"features/techdocs/architecture",
"features/techdocs/creating-and-publishing",
"features/techdocs/configuration",
"features/techdocs/using-cloud-storage",
"features/techdocs/configuring-ci-cd",
"features/techdocs/cli",
"features/techdocs/how-to-guides",
"features/techdocs/troubleshooting",
"features/techdocs/faqs"
]
}
],
"Integrations": [
"integrations/index",
{
"type": "category",
"label": "AWS S3",
"items": [
"integrations/aws-s3/locations",
"integrations/aws-s3/discovery"
]
},
{
"type": "category",
"label": "Azure",
"items": [
"integrations/azure/locations",
"integrations/azure/discovery",
"integrations/azure/org"
]
},
{
"type": "category",
"label": "Bitbucket Cloud",
"items": [
"integrations/bitbucketCloud/locations",
"integrations/bitbucketCloud/discovery"
]
},
{
"type": "category",
"label": "Bitbucket Server",
"items": [
"integrations/bitbucketServer/locations",
"integrations/bitbucketServer/discovery"
]
},
{
"type": "category",
"label": "Datadog",
"items": ["integrations/datadog-rum/installation"]
},
{
"type": "category",
"label": "Gerrit",
"items": [
"integrations/gerrit/locations",
"integrations/gerrit/discovery"
]
},
{
"type": "category",
"label": "GitHub",
"items": [
"integrations/github/locations",
"integrations/github/discovery",
"integrations/github/org",
"integrations/github/github-apps"
]
},
{
"type": "category",
"label": "GitLab",
"items": [
"integrations/gitlab/locations",
"integrations/gitlab/discovery"
]
},
{
"type": "category",
"label": "Gitea",
"items": ["integrations/gitea/locations"]
},
{
"type": "category",
"label": "Google GCS",
"items": ["integrations/google-cloud-storage/locations"]
},
{
"type": "category",
"label": "LDAP",
"items": ["integrations/ldap/org"]
}
],
"Plugins": [
"plugins/index",
"plugins/existing-plugins",
"plugins/create-a-plugin",
"plugins/plugin-development",
"plugins/structure-of-a-plugin",
"plugins/integrating-plugin-into-software-catalog",
"plugins/integrating-search-into-plugins",
"plugins/composability",
"plugins/customization",
"plugins/analytics",
"plugins/feature-flags",
{
"type": "category",
"label": "Backends and APIs",
"items": [
"plugins/proxying",
"plugins/backend-plugin",
"plugins/call-existing-api",
"plugins/url-reader",
"plugins/new-backend-system"
]
},
{
"type": "category",
"label": "Testing",
"items": ["plugins/testing"]
},
{
"type": "category",
"label": "Publishing",
"items": [
"plugins/publish-private",
"plugins/add-to-marketplace",
"plugins/observability"
]
}
],
"Configuration": [
"conf/index",
"conf/reading",
"conf/writing",
"conf/defining"
],
"Auth and identity": [
"auth/index",
{
"type": "category",
"label": "Included providers",
"items": [
"auth/auth0/provider",
"auth/atlassian/provider",
"auth/bitbucket/provider",
"auth/microsoft/provider",
"auth/github/provider",
"auth/gitlab/provider",
"auth/google/provider",
"auth/google/gcp-iap-auth",
"auth/okta/provider",
"auth/onelogin/provider",
"auth/oauth2-proxy/provider"
]
},
"auth/identity-resolver",
"auth/oauth",
"auth/oidc",
"auth/add-auth-provider",
"auth/service-to-service-auth",
"auth/troubleshooting",
"auth/glossary"
],
"Permissions": [
"permissions/overview",
"permissions/concepts",
"permissions/getting-started",
"permissions/writing-a-policy",
"permissions/frontend-integration",
"permissions/custom-rules",
{
"type": "category",
"label": "Tutorial: using Permissions in your plugin",
"items": [
"permissions/plugin-authors/01-setup",
"permissions/plugin-authors/02-adding-a-basic-permission-check",
"permissions/plugin-authors/03-adding-a-resource-permission-check",
"permissions/plugin-authors/04-authorizing-access-to-paginated-data",
"permissions/plugin-authors/05-frontend-authorization"
]
}
],
"Deployment": [
"deployment/index",
"deployment/scaling",
"deployment/docker",
"deployment/k8s",
"deployment/heroku"
],
"Designing for Backstage": [
"dls/design",
"dls/component-design-guidelines",
"dls/contributing-to-storybook",
"dls/figma"
],
"API Reference": [
{
"type": "category",
"label": "Guides",
"items": ["api/utility-apis"]
},
{
"type": "category",
"label": "API Reference",
"items": ["reference/index"]
},
"api/deprecations"
],
"Tutorials": [
"tutorials/journey",
"tutorials/quickstart-app-plugin",
"tutorials/react-router-stable-migration",
"tutorials/package-role-migration",
"tutorials/migrating-away-from-core",
"tutorials/configuring-plugin-databases",
"tutorials/switching-sqlite-postgres",
"tutorials/using-backstage-proxy-within-plugin",
"tutorials/yarn-migration"
],
"Architecture Decision Records (ADRs)": [
"architecture-decisions/adrs-overview",
"architecture-decisions/adrs-adr001",
"architecture-decisions/adrs-adr002",
"architecture-decisions/adrs-adr003",
"architecture-decisions/adrs-adr004",
"architecture-decisions/adrs-adr005",
"architecture-decisions/adrs-adr006",
"architecture-decisions/adrs-adr007",
"architecture-decisions/adrs-adr008",
"architecture-decisions/adrs-adr009",
"architecture-decisions/adrs-adr010",
"architecture-decisions/adrs-adr011",
"architecture-decisions/adrs-adr012",
"architecture-decisions/adrs-adr013"
],
"FAQ": ["FAQ"]
}
}
+129 -5
View File
@@ -3573,6 +3573,7 @@ __metadata:
prettier: ^2.6.2
react: ^17.0.2
react-dom: ^17.0.2
replace: ^1.2.2
languageName: unknown
linkType: soft
@@ -3827,6 +3828,13 @@ __metadata:
languageName: node
linkType: hard
"camelcase@npm:^5.0.0":
version: 5.3.1
resolution: "camelcase@npm:5.3.1"
checksum: e6effce26b9404e3c0f301498184f243811c30dfe6d0b9051863bd8e4034d09c8c2923794f280d6827e5aa055f6c434115ff97864a16a963366fb35fd673024b
languageName: node
linkType: hard
"camelcase@npm:^6.2.0":
version: 6.3.0
resolution: "camelcase@npm:6.3.0"
@@ -3860,7 +3868,7 @@ __metadata:
languageName: node
linkType: hard
"chalk@npm:^2.0.0":
"chalk@npm:2.4.2, chalk@npm:^2.0.0":
version: 2.4.2
resolution: "chalk@npm:2.4.2"
dependencies:
@@ -4021,6 +4029,17 @@ __metadata:
languageName: node
linkType: hard
"cliui@npm:^6.0.0":
version: 6.0.0
resolution: "cliui@npm:6.0.0"
dependencies:
string-width: ^4.2.0
strip-ansi: ^6.0.0
wrap-ansi: ^6.2.0
checksum: 4fcfd26d292c9f00238117f39fc797608292ae36bac2168cfee4c85923817d0607fe21b3329a8621e01aedf512c99b7eaa60e363a671ffd378df6649fb48ae42
languageName: node
linkType: hard
"clone-deep@npm:^4.0.1":
version: 4.0.1
resolution: "clone-deep@npm:4.0.1"
@@ -4597,6 +4616,13 @@ __metadata:
languageName: node
linkType: hard
"decamelize@npm:^1.2.0":
version: 1.2.0
resolution: "decamelize@npm:1.2.0"
checksum: ad8c51a7e7e0720c70ec2eeb1163b66da03e7616d7b98c9ef43cce2416395e84c1e9548dd94f5f6ffecfee9f8b94251fc57121a8b021f2ff2469b2bae247b8aa
languageName: node
linkType: hard
"decompress-response@npm:^3.3.0":
version: 3.3.0
resolution: "decompress-response@npm:3.3.0"
@@ -5355,7 +5381,7 @@ __metadata:
languageName: node
linkType: hard
"find-up@npm:^4.0.0":
"find-up@npm:^4.0.0, find-up@npm:^4.1.0":
version: 4.1.0
resolution: "find-up@npm:4.1.0"
dependencies:
@@ -5544,6 +5570,13 @@ __metadata:
languageName: node
linkType: hard
"get-caller-file@npm:^2.0.1":
version: 2.0.5
resolution: "get-caller-file@npm:2.0.5"
checksum: b9769a836d2a98c3ee734a88ba712e62703f1df31b94b784762c433c27a386dd6029ff55c2a920c392e33657d80191edbf18c61487e198844844516f843496b9
languageName: node
linkType: hard
"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1":
version: 1.1.3
resolution: "get-intrinsic@npm:1.1.3"
@@ -6716,11 +6749,11 @@ __metadata:
linkType: hard
"json5@npm:^2.1.2, json5@npm:^2.2.1":
version: 2.2.1
resolution: "json5@npm:2.2.1"
version: 2.2.3
resolution: "json5@npm:2.2.3"
bin:
json5: lib/cli.js
checksum: 74b8a23b102a6f2bf2d224797ae553a75488b5adbaee9c9b6e5ab8b510a2fc6e38f876d4c77dea672d4014a44b2399e15f2051ac2b37b87f74c0c7602003543b
checksum: 2a7436a93393830bce797d4626275152e37e877b265e94ca69c99e3d20c2b9dab021279146a39cdb700e71b2dd32a4cebd1514cd57cee102b1af906ce5040349
languageName: node
linkType: hard
@@ -7164,6 +7197,15 @@ __metadata:
languageName: node
linkType: hard
"minimatch@npm:3.0.5":
version: 3.0.5
resolution: "minimatch@npm:3.0.5"
dependencies:
brace-expansion: ^1.1.7
checksum: a3b84b426eafca947741b864502cee02860c4e7b145de11ad98775cfcf3066fef422583bc0ffce0952ddf4750c1ccf4220b1556430d4ce10139f66247d87d69e
languageName: node
linkType: hard
"minimatch@npm:3.1.2, minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1":
version: 3.1.2
resolution: "minimatch@npm:3.1.2"
@@ -8982,6 +9024,27 @@ __metadata:
languageName: node
linkType: hard
"replace@npm:^1.2.2":
version: 1.2.2
resolution: "replace@npm:1.2.2"
dependencies:
chalk: 2.4.2
minimatch: 3.0.5
yargs: ^15.3.1
bin:
replace: bin/replace.js
search: bin/search.js
checksum: 1d69f43937a5fdf9dea278e78d6f3b51c1889ba5135bd201918bbda6330684adf8276e8e90e1c021034420dd4df239e51c23ca40752cb9bc6180c153d6d46a37
languageName: node
linkType: hard
"require-directory@npm:^2.1.1":
version: 2.1.1
resolution: "require-directory@npm:2.1.1"
checksum: fb47e70bf0001fdeabdc0429d431863e9475e7e43ea5f94ad86503d918423c1543361cc5166d713eaa7029dd7a3d34775af04764bebff99ef413111a5af18c80
languageName: node
linkType: hard
"require-from-string@npm:^2.0.2":
version: 2.0.2
resolution: "require-from-string@npm:2.0.2"
@@ -8996,6 +9059,13 @@ __metadata:
languageName: node
linkType: hard
"require-main-filename@npm:^2.0.0":
version: 2.0.0
resolution: "require-main-filename@npm:2.0.0"
checksum: e9e294695fea08b076457e9ddff854e81bffbe248ed34c1eec348b7abbd22a0d02e8d75506559e2265e96978f3c4720bd77a6dad84755de8162b357eb6c778c7
languageName: node
linkType: hard
"requires-port@npm:^1.0.0":
version: 1.0.0
resolution: "requires-port@npm:1.0.0"
@@ -10623,6 +10693,13 @@ __metadata:
languageName: node
linkType: hard
"which-module@npm:^2.0.0":
version: 2.0.0
resolution: "which-module@npm:2.0.0"
checksum: 809f7fd3dfcb2cdbe0180b60d68100c88785084f8f9492b0998c051d7a8efe56784492609d3f09ac161635b78ea29219eb1418a98c15ce87d085bce905705c9c
languageName: node
linkType: hard
"which@npm:^1.3.1":
version: 1.3.1
resolution: "which@npm:1.3.1"
@@ -10679,6 +10756,17 @@ __metadata:
languageName: node
linkType: hard
"wrap-ansi@npm:^6.2.0":
version: 6.2.0
resolution: "wrap-ansi@npm:6.2.0"
dependencies:
ansi-styles: ^4.0.0
string-width: ^4.1.0
strip-ansi: ^6.0.0
checksum: 6cd96a410161ff617b63581a08376f0cb9162375adeb7956e10c8cd397821f7eb2a6de24eb22a0b28401300bf228c86e50617cd568209b5f6775b93c97d2fe3a
languageName: node
linkType: hard
"wrap-ansi@npm:^7.0.0":
version: 7.0.0
resolution: "wrap-ansi@npm:7.0.0"
@@ -10775,6 +10863,13 @@ __metadata:
languageName: node
linkType: hard
"y18n@npm:^4.0.0":
version: 4.0.3
resolution: "y18n@npm:4.0.3"
checksum: 014dfcd9b5f4105c3bb397c1c8c6429a9df004aa560964fb36732bfb999bfe83d45ae40aeda5b55d21b1ee53d8291580a32a756a443e064317953f08025b1aa4
languageName: node
linkType: hard
"yallist@npm:^4.0.0":
version: 4.0.0
resolution: "yallist@npm:4.0.0"
@@ -10789,6 +10884,35 @@ __metadata:
languageName: node
linkType: hard
"yargs-parser@npm:^18.1.2":
version: 18.1.3
resolution: "yargs-parser@npm:18.1.3"
dependencies:
camelcase: ^5.0.0
decamelize: ^1.2.0
checksum: 60e8c7d1b85814594d3719300ecad4e6ae3796748b0926137bfec1f3042581b8646d67e83c6fc80a692ef08b8390f21ddcacb9464476c39bbdf52e34961dd4d9
languageName: node
linkType: hard
"yargs@npm:^15.3.1":
version: 15.4.1
resolution: "yargs@npm:15.4.1"
dependencies:
cliui: ^6.0.0
decamelize: ^1.2.0
find-up: ^4.1.0
get-caller-file: ^2.0.1
require-directory: ^2.1.1
require-main-filename: ^2.0.0
set-blocking: ^2.0.0
string-width: ^4.2.0
which-module: ^2.0.0
y18n: ^4.0.0
yargs-parser: ^18.1.2
checksum: 40b974f508d8aed28598087720e086ecd32a5fd3e945e95ea4457da04ee9bdb8bdd17fd91acff36dc5b7f0595a735929c514c40c402416bbb87c03f6fb782373
languageName: node
linkType: hard
"yocto-queue@npm:^0.1.0":
version: 0.1.0
resolution: "yocto-queue@npm:0.1.0"
@@ -0,0 +1,79 @@
---
title: Backstage Wrapped 2022
author: Francesco Corti & Suzanne Daniels
authorURL: https://github.com/fcorti
---
![wrapped header image](assets/2022-12-19/wrapped.png)
If 2021 was the year of growth for the Backstage project and community, 2022 can be easily defined as the year of maturity. Both the project and the community continued to grow at an incredible pace and the platform is now more stable, more secure, and more powerful than ever.
Our community is full of people from across the globe who bring incredible engagement and positive vibes to the project. And we finally got to experience this for the first time in real life during [BackstageCon](https://www.youtube.com/playlist?list=PLj6h78yzYM2OKySsTuiip3BqmdYZQRnSf), the very first in-person conference fully dedicated to Backstage, where hundreds of enthusiasts, the maintainers, adopters, and partners joined the event confirming the great momentum and the traction in the market.
But before anticipating too much, lets share some stats of this amazing 2022.
<!--truncate-->
## We grew … a lot!
Last year we celebrated 75 [public adopters](https://github.com/backstage/backstage/blob/master/ADOPTERS.md) and today we have over 600 adopters, confirming the interest around Backstage across all the industries, sectors, and dimensions. Companies like [Toyota North America](https://aws.amazon.com/solutions/case-studies/toyota-ecs-case-study/), Deutsche Bank, Mercedes-Benz, Procter & Gamble, Siemens, LinkedIn, and many more joined the community this year and are actively contributing in growing the product and its maturity.
![Accelerated growth of Public Backstage Adopters, from January 2021December 2022.](assets/2022-12-19/public-backstage-adopters-2022.png)
Not only are our adopters growing at an exponential rate, but so too are our contributors. Our [Discord server](https://discord.gg/backstage-687207715902193673) is a perfect example of the worlds of our adopters and contributors melding. On average, at any given hour of the day or night, there are more than 500 people online available to talk and provide support on any Backstage topic. Discord discussions run a full gamut of topics from technical support questions to best practices for onboarding engineers to Backstage basically, anything and everything an adopter may be interested to know and learn. At the very beginning of 2022, we “only” had 4k Discord members. Today, we have a growing membership of 8.2k.
Isnt it what we can call a great and vibrant community of enthusiasts?
The growth of the ecosystem is not limited to the adopters and contributors. The ecosystem also includes big tech companies interested in growing Backstage by including it as part of their offering and integrations. Our [collaboration with VMWare](https://youtube.com/watch?v=GAzKCQO8Vt0&si=EnSIkaIECMiOmarE) is a great example of this, and [RedHat](https://developers.redhat.com/articles/2022/10/24/red-hat-joins-backstageio-community) joined the party this year promising even faster growth in contributions and integrations for the Backstage project.
Before we go any further, lets take a quick look at some more numbers, mainly coming from the [Official Github repository](https://github.com/backstage/backstage). This year…
- Our contributors grew from a group of 500 to 900+
- Contributions to the project increased from 9k to 15k+
- The number of forks shot up from 2k to 3k+
- Backstage stars increased from 14k to almost 20k
- And lastly, we welcomed over 550 adopters into our community.
All of these people driving the enormous growth and maturity of the project are represented in the chart below, which shows cumulative activities per month for Backstage:
![Main Github metrics for Backstage.io.](assets/2022-12-19/github-metrics-for-backstage-io.png)
Wow… thats a lot of activity. Weve generated this animation showing all of your contributions to the Backstage repository in 2022, look at this project grow:
<iframe width="780" height="440" src="https://www.youtube.com/embed/GoWBvHpH5Cg" frameBorder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowFullScreen></iframe>
## But wait… theres more
The growth of the community is so exciting to see. And with all this community growth, comes growth and maturity of the product, making Backstage better able to support adopters in their developer experience challenges. The Backstage open source project has almost [100 plugins](https://backstage.io/plugins), each one amending Backstage with a tool or service to improve not only adopters Backstage apps, but their unique developer experiences. And almost every week we learn of a new one being contributed, which is amazing.
The Backstage platform itself is leaving 2022 more mature than ever, thanks to milestones such as these:
- [Backstage is now beyond version 1](https://backstage.io/blog/2022/03/17/backstage-1.0).
- [Backstage had its first Security Audit ever and now can rely on a Threat Model](https://backstage.io/blog/2022/08/23/backstage-security-audit).
- [Backstage Learn helps adopters get started more easily](https://backstage.spotify.com/learn/).
- [Backstage reached incubating status as part of the CNCF ecosystem](https://www.cncf.io/blog/2022/03/15/backstage-project-joins-the-cncf-incubator/).
And like every mature project, now Backstage has also a mascot: Bowie the Beaver!
![Bowie the Beaver.](assets/2022-12-19/bowie-the-beaver.png)
Additionally, we launched a new [Special Interest Groups (SIGs)](https://github.com/backstage/community/tree/main/sigs) initiative this year, following the lead of Kubernetes. We believe Backstage SIGs will better coordinate contributions, grow the platform cohesively with the open source community, and allow contributors to dig even deeper as a community member. At this moment in time there two SIGs in place:
- The [Catalog SIG](https://github.com/backstage/community/blob/main/sigs/sig-catalog/README.md), covering all aspects of the systems and components related to the Software Catalog.
- The [Adoption SIG](https://github.com/backstage/community/blob/main/sigs/sig-adoption/README.md), covering all aspects of the adoption journey of Backstage.
Our plan is to open more SIGs in 2023 to engage more with the community and to keep driving towards cohesive growth for the community and project.
Last but not least, we mustn't overlook the first ever conference fully dedicated to Backstage: BackstageCon. What was special there? SO much. But in particular two things: seeing everyone in real life and the incredible mood and conversations between maintainers, adopters, partners and enthusiasts. Its hard to express just how positive and amazing the vibes were in a blog, but you can read our recap [here](https://backstage.io/blog/2022/10/28/backstagecon-kubecon-2022) or watch the sessions [here](https://www.youtube.com/playlist?list=PLj6h78yzYM2OKySsTuiip3BqmdYZQRnSf). And in the meantime, here are some great numbers from the event:
- 250 attendees or a 100% full venue and packed room.
- 14 sessions
- 17 speakers
## Goodbye, 2022. Hello, 2023!
After this incredible year, thanks to everyone in the global Backstage community who contributed to the open source project in 2022. We are already working on the goals for 2023 and as a roadmap teaser: we are very much committed to making the journey of adoption easier, faster, more valuable, and overall better. We would not be here today without our amazing community, so thank you for everything!
Looking forward to continuing collaborating together for an exciting 2023.
See you then! Happy holidays from everyone on the Backstage team at Spotify!
Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 KiB

+10
View File
@@ -0,0 +1,10 @@
---
title: AWS CodeSuite
author: Amazon Web Services
authorUrl: https://aws.amazon.com/
category: CI/CD
description: View resources related to AWS CodePipeline, AWS CodeBuild and AWS CodeDeploy for your components in Backstage.
documentation: https://github.com/awslabs/aws-codesuite-plugins-for-backstage#readme
iconUrl: https://github.com/awslabs/aws-codesuite-plugins-for-backstage/blob/main/docs/images/codepipeline-logo.png?raw=true
npmPackageName: '@aws/aws-codesuite-plugin-for-backstage'
addedDate: '2023-01-17'

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