diff --git a/.changeset/afraid-gorillas-beg.md b/.changeset/afraid-gorillas-beg.md
new file mode 100644
index 0000000000..fdaf03502a
--- /dev/null
+++ b/.changeset/afraid-gorillas-beg.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-permission-react': minor
+---
+
+**BREAKING**: Update to use renamed request and response types from @backstage/plugin-permission-common.
diff --git a/.changeset/beige-crabs-itch.md b/.changeset/beige-crabs-itch.md
new file mode 100644
index 0000000000..9c66e42054
--- /dev/null
+++ b/.changeset/beige-crabs-itch.md
@@ -0,0 +1,6 @@
+---
+'@backstage/core-components': patch
+'@backstage/plugin-api-docs': patch
+---
+
+chore(deps): bump `react-syntax-highligher` and `swagger-ui-react`
diff --git a/.changeset/big-moles-visit.md b/.changeset/big-moles-visit.md
new file mode 100644
index 0000000000..e1af13bc55
--- /dev/null
+++ b/.changeset/big-moles-visit.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-permission-backend': minor
+---
+
+**BREAKING**: Wrap batched requests and responses to /authorize in an envelope object. The latest version of the PermissionClient in @backstage/permission-common uses the new format - as long as the permission-backend is consumed using this client, no other changes are necessary.
diff --git a/.changeset/chilled-cats-marry.md b/.changeset/chilled-cats-marry.md
new file mode 100644
index 0000000000..9c7617de33
--- /dev/null
+++ b/.changeset/chilled-cats-marry.md
@@ -0,0 +1,7 @@
+---
+'@backstage/plugin-permission-common': minor
+---
+
+**BREAKING**: Authorize API request and response types have been updated. The existing `AuthorizeRequest` and `AuthorizeResponse` types now match the entire request and response objects for the /authorize endpoint, and new types `AuthorizeQuery` and `AuthorizeDecision` have been introduced for individual items in the request and response batches respectively.
+
+**BREAKING**: PermissionClient has been updated to use the new request and response format in the latest version of @backstage/permission-backend.
diff --git a/.changeset/five-peaches-guess.md b/.changeset/five-peaches-guess.md
new file mode 100644
index 0000000000..bc278cc665
--- /dev/null
+++ b/.changeset/five-peaches-guess.md
@@ -0,0 +1,13 @@
+---
+'@backstage/plugin-org': patch
+---
+
+For the component `EntityMembersListCard` you can now specify the pageSize. For example:
+
+```tsx
+
+
+
+```
+
+If left empty it will by default use 50.
diff --git a/.changeset/fluffy-countries-beam.md b/.changeset/fluffy-countries-beam.md
new file mode 100644
index 0000000000..f542cfd33b
--- /dev/null
+++ b/.changeset/fluffy-countries-beam.md
@@ -0,0 +1,10 @@
+---
+'@backstage/plugin-catalog': patch
+'@backstage/plugin-cost-insights': patch
+'@backstage/plugin-shortcuts': patch
+'@backstage/plugin-user-settings': patch
+---
+
+**@backstage/plugin-user-settings:** Hide Header on mobile screens to improve the UI & give more space to the content. Furthermore, the "Pin Sidebar" setting is removed on mobile screens, as the mobile sidebar is always pinned to the bottom.
+
+**Other plugins:** Smaller style adjustments across plugins to improve the UI on mobile devices.
diff --git a/.changeset/great-bulldogs-provide.md b/.changeset/great-bulldogs-provide.md
new file mode 100644
index 0000000000..3b51ea9bdf
--- /dev/null
+++ b/.changeset/great-bulldogs-provide.md
@@ -0,0 +1,11 @@
+---
+'@backstage/core-components': patch
+---
+
+The `Bar` component will now render a `MobileSidebar` instead of the current sidebar on smaller screens. The state of the `MobileSidebar` will be treated as always open.
+
+---
+
+**Add MobileSidebar:** A navigation component, which sticks to the bottom. If there is no content in the Sidebar, it won't be rendered. If there are `children ` in the `Sidebar`, but no `SidebarGroup`s as `children`, it will render all `children` into a default overlay menu, which can be displayed by clicking a menu item. If `SidebarGroup`s are provided, it will render them in the bottom navigation. Additionally, a `MobileSidebarContext`, which wraps the component, will save the selected menu item.
+
+**Add SidebarGroup:** Groups items of the `Sidebar` together. On bigger screens, this won't have any effect at the moment. On smaller screens, it will render a given icon into the `MobileSidebar`. If a route is provided, clicking the `SidebarGroup` in the `MobileSidebar` will route to the page. If no route is provided, it will add a provided icon to the `MobileSidebar` as a menu item & will render the children into an overlay menu, which will be displayed when the menu item is clicked.
diff --git a/.changeset/green-candles-remember.md b/.changeset/green-candles-remember.md
new file mode 100644
index 0000000000..2edec229e4
--- /dev/null
+++ b/.changeset/green-candles-remember.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog-backend': patch
+---
+
+Now when entities are deleted, the parent entity state is updated such that it will "heal" accidental deletes on the next refresh round.
diff --git a/.changeset/late-carrots-end.md b/.changeset/late-carrots-end.md
new file mode 100644
index 0000000000..9971baffdc
--- /dev/null
+++ b/.changeset/late-carrots-end.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-org': patch
+---
+
+Fixed typo in `MembersListCard` component
diff --git a/.changeset/light-trainers-allow.md b/.changeset/light-trainers-allow.md
new file mode 100644
index 0000000000..0ea13cd19e
--- /dev/null
+++ b/.changeset/light-trainers-allow.md
@@ -0,0 +1,22 @@
+---
+'@backstage/backend-common': patch
+'@backstage/config': patch
+'@backstage/config-loader': patch
+'@backstage/plugin-app-backend': patch
+---
+
+Loading of app configurations now reference the `@deprecated` construct from
+JSDoc to determine if a property in-use has been deprecated. Users are notified
+of deprecated keys in the format:
+
+```txt
+The configuration key 'catalog.processors.githubOrg' of app-config.yaml is deprecated and may be removed soon. Configure a GitHub integration instead.
+```
+
+When the `withDeprecatedKeys` option is set to `true` in the `process` method
+of `loadConfigSchema`, the user will be notified that deprecated keys have been
+identified in their app configuration.
+
+The `backend-common` and `plugin-app-backend` packages have been updated to set
+`withDeprecatedKeys` to true so that users are notified of deprecated settings
+by default.
diff --git a/.changeset/long-clouds-rest.md b/.changeset/long-clouds-rest.md
new file mode 100644
index 0000000000..701be91536
--- /dev/null
+++ b/.changeset/long-clouds-rest.md
@@ -0,0 +1,12 @@
+---
+'@backstage/cli': patch
+---
+
+Introduce `--deprecated` option to `config:check` to log all deprecated app configuration properties
+
+```sh
+$ yarn backstage-cli config:check --lax --deprecated
+config:check --lax --deprecated
+Loaded config from app-config.yaml
+The configuration key 'catalog.processors.githubOrg' of app-config.yaml is deprecated and may be removed soon. Configure a GitHub integration instead.
+```
diff --git a/.changeset/pink-actors-poke.md b/.changeset/pink-actors-poke.md
new file mode 100644
index 0000000000..e35a925e8c
--- /dev/null
+++ b/.changeset/pink-actors-poke.md
@@ -0,0 +1,6 @@
+---
+'@backstage/plugin-permission-node': minor
+---
+
+**BREAKING**: `PolicyAuthorizeRequest` type has been renamed to `PolicyAuthorizeQuery`.
+**BREAKING**: Update to use renamed request and response types from @backstage/plugin-permission-common.
diff --git a/.changeset/pre.json b/.changeset/pre.json
new file mode 100644
index 0000000000..5f0d5e837d
--- /dev/null
+++ b/.changeset/pre.json
@@ -0,0 +1,128 @@
+{
+ "mode": "pre",
+ "tag": "next",
+ "initialVersions": {
+ "example-app": "0.2.60",
+ "@backstage/app-defaults": "0.1.4",
+ "example-backend": "0.2.60",
+ "@backstage/backend-common": "0.10.3",
+ "@backstage/backend-tasks": "0.1.3",
+ "@backstage/backend-test-utils": "0.1.13",
+ "@backstage/catalog-client": "0.5.4",
+ "@backstage/catalog-model": "0.9.9",
+ "@backstage/cli": "0.11.0",
+ "@backstage/cli-common": "0.1.6",
+ "@backstage/codemods": "0.1.29",
+ "@backstage/config": "0.1.12",
+ "@backstage/config-loader": "0.9.2",
+ "@backstage/core-app-api": "0.4.0",
+ "@backstage/core-components": "0.8.4",
+ "@backstage/core-plugin-api": "0.5.0",
+ "@backstage/create-app": "0.4.12",
+ "@backstage/dev-utils": "0.2.17",
+ "e2e-test": "0.2.0",
+ "embedded-techdocs-app": "0.2.59",
+ "@backstage/errors": "0.2.0",
+ "@backstage/integration": "0.7.1",
+ "@backstage/integration-react": "0.1.18",
+ "@backstage/search-common": "0.2.1",
+ "storybook": "0.2.1",
+ "@techdocs/cli": "0.8.10",
+ "@backstage/techdocs-common": "0.11.3",
+ "@backstage/test-utils": "0.2.2",
+ "@backstage/theme": "0.2.14",
+ "@backstage/types": "0.1.1",
+ "@backstage/version-bridge": "0.1.1",
+ "@backstage/plugin-airbrake": "0.1.0",
+ "@backstage/plugin-allure": "0.1.11",
+ "@backstage/plugin-analytics-module-ga": "0.1.6",
+ "@backstage/plugin-apache-airflow": "0.1.3",
+ "@backstage/plugin-api-docs": "0.6.22",
+ "@backstage/plugin-app-backend": "0.3.21",
+ "@backstage/plugin-auth-backend": "0.6.2",
+ "@backstage/plugin-azure-devops": "0.1.10",
+ "@backstage/plugin-azure-devops-backend": "0.3.0",
+ "@backstage/plugin-azure-devops-common": "0.2.0",
+ "@backstage/plugin-badges": "0.2.19",
+ "@backstage/plugin-badges-backend": "0.1.15",
+ "@backstage/plugin-bazaar": "0.1.9",
+ "@backstage/plugin-bazaar-backend": "0.1.6",
+ "@backstage/plugin-bitrise": "0.1.22",
+ "@backstage/plugin-catalog": "0.7.8",
+ "@backstage/plugin-catalog-backend": "0.20.0",
+ "@backstage/plugin-catalog-backend-module-ldap": "0.3.9",
+ "@backstage/plugin-catalog-backend-module-msgraph": "0.2.12",
+ "@backstage/plugin-catalog-common": "0.1.0",
+ "@backstage/plugin-catalog-graph": "0.2.6",
+ "@backstage/plugin-catalog-graphql": "0.3.0",
+ "@backstage/plugin-catalog-import": "0.7.9",
+ "@backstage/plugin-catalog-react": "0.6.11",
+ "@backstage/plugin-circleci": "0.2.34",
+ "@backstage/plugin-cloudbuild": "0.2.32",
+ "@backstage/plugin-code-coverage": "0.1.22",
+ "@backstage/plugin-code-coverage-backend": "0.1.19",
+ "@backstage/plugin-config-schema": "0.1.18",
+ "@backstage/plugin-cost-insights": "0.11.17",
+ "@backstage/plugin-explore": "0.3.25",
+ "@backstage/plugin-explore-react": "0.0.10",
+ "@backstage/plugin-firehydrant": "0.1.12",
+ "@backstage/plugin-fossa": "0.2.27",
+ "@backstage/plugin-gcp-projects": "0.3.13",
+ "@backstage/plugin-git-release-manager": "0.3.8",
+ "@backstage/plugin-github-actions": "0.4.31",
+ "@backstage/plugin-github-deployments": "0.1.26",
+ "@backstage/plugin-gitops-profiles": "0.3.13",
+ "@backstage/plugin-gocd": "0.1.1",
+ "@backstage/plugin-graphiql": "0.2.27",
+ "@backstage/plugin-graphql-backend": "0.1.11",
+ "@backstage/plugin-home": "0.4.10",
+ "@backstage/plugin-ilert": "0.1.21",
+ "@backstage/plugin-jenkins": "0.5.17",
+ "@backstage/plugin-jenkins-backend": "0.1.10",
+ "@backstage/plugin-kafka": "0.2.25",
+ "@backstage/plugin-kafka-backend": "0.2.14",
+ "@backstage/plugin-kubernetes": "0.5.4",
+ "@backstage/plugin-kubernetes-backend": "0.4.3",
+ "@backstage/plugin-kubernetes-common": "0.2.1",
+ "@backstage/plugin-lighthouse": "0.2.34",
+ "@backstage/plugin-newrelic": "0.3.13",
+ "@backstage/plugin-newrelic-dashboard": "0.1.3",
+ "@backstage/plugin-org": "0.3.34",
+ "@backstage/plugin-pagerduty": "0.3.22",
+ "@backstage/plugin-permission-backend": "0.3.0",
+ "@backstage/plugin-permission-common": "0.3.1",
+ "@backstage/plugin-permission-node": "0.3.0",
+ "@backstage/plugin-permission-react": "0.2.2",
+ "@backstage/plugin-proxy-backend": "0.2.15",
+ "@backstage/plugin-rollbar": "0.3.23",
+ "@backstage/plugin-rollbar-backend": "0.1.18",
+ "@backstage/plugin-scaffolder": "0.11.18",
+ "@backstage/plugin-scaffolder-backend": "0.15.20",
+ "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.1.8",
+ "@backstage/plugin-scaffolder-backend-module-rails": "0.2.3",
+ "@backstage/plugin-scaffolder-backend-module-yeoman": "0.1.2",
+ "@backstage/plugin-scaffolder-common": "0.1.2",
+ "@backstage/plugin-search": "0.5.5",
+ "@backstage/plugin-search-backend": "0.3.0",
+ "@backstage/plugin-search-backend-module-elasticsearch": "0.0.7",
+ "@backstage/plugin-search-backend-module-pg": "0.2.3",
+ "@backstage/plugin-search-backend-node": "0.4.4",
+ "@backstage/plugin-sentry": "0.3.33",
+ "@backstage/plugin-shortcuts": "0.1.19",
+ "@backstage/plugin-sonarqube": "0.2.12",
+ "@backstage/plugin-splunk-on-call": "0.3.19",
+ "@backstage/plugin-tech-insights": "0.1.5",
+ "@backstage/plugin-tech-insights-backend": "0.1.5",
+ "@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.5",
+ "@backstage/plugin-tech-insights-common": "0.2.1",
+ "@backstage/plugin-tech-insights-node": "0.1.2",
+ "@backstage/plugin-tech-radar": "0.5.2",
+ "@backstage/plugin-techdocs": "0.12.14",
+ "@backstage/plugin-techdocs-backend": "0.12.3",
+ "@backstage/plugin-todo": "0.1.18",
+ "@backstage/plugin-todo-backend": "0.1.18",
+ "@backstage/plugin-user-settings": "0.3.16",
+ "@backstage/plugin-xcmetrics": "0.2.15"
+ },
+ "changesets": []
+}
diff --git a/.changeset/red-meals-turn.md b/.changeset/red-meals-turn.md
new file mode 100644
index 0000000000..9d52b61642
--- /dev/null
+++ b/.changeset/red-meals-turn.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-techdocs': patch
+---
+
+use lighter color for block quotes and horizontal rulers
diff --git a/.changeset/sour-spies-hug.md b/.changeset/sour-spies-hug.md
new file mode 100644
index 0000000000..eb6b860922
--- /dev/null
+++ b/.changeset/sour-spies-hug.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-api-docs': patch
+---
+
+Using an explicitly empty string for the url argument ensures that the swagger UI does not run into undefined errors.
diff --git a/.changeset/stale-brooms-fry.md b/.changeset/stale-brooms-fry.md
new file mode 100644
index 0000000000..18c847973e
--- /dev/null
+++ b/.changeset/stale-brooms-fry.md
@@ -0,0 +1,51 @@
+---
+'@backstage/create-app': patch
+---
+
+You can now add `SidebarGroup`s to the current `Sidebar`. This will not affect how the current sidebar is displayed, but allows a customization on how the `MobileSidebar` on smaller screens will look like. A `SidebarGroup` will be displayed with the given icon in the `MobileSidebar`.
+
+A `SidebarGroup` can either link to an existing page (e.g. `/search` or `/settings`) or wrap components, which will be displayed in a full-screen overlay menu (e.g. `Menu`).
+
+```diff
+
+
++ } to="/search">
+
++
+
++ }>
+
+
+
+
+
+
++
+
+
++ }
++ to="/settings"
++ >
+
++
+
+```
+
+Additionally, you can order the groups differently in the `MobileSidebar` than in the usual `Sidebar` simply by giving a group a priority. The groups will be displayed in descending order from left to right.
+
+```diff
+}
+ to="/settings"
++ priority={1}
+>
+
+
+```
+
+If you decide against adding `SidebarGroup`s to your `Sidebar` the `MobileSidebar` will contain one default menu item, which will open a full-screen overlay menu displaying all the content of the current `Sidebar`.
+
+More information on the `SidebarGroup` & the `MobileSidebar` component can be found in the changeset for the `core-components`.
diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml
index 2a6e3dc6f9..7036f99c57 100644
--- a/.github/workflows/master.yml
+++ b/.github/workflows/master.yml
@@ -192,7 +192,12 @@ jobs:
# Publishes current version of packages that are not already present in the registry
- name: publish
- run: yarn lerna -- publish from-package --yes
+ run: |
+ if [ -f ".changeset/pre.json" ]; then
+ yarn lerna -- publish from-package --yes --dist-tag next
+ else
+ yarn lerna -- publish from-package --yes
+ fi
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/contrib/chart/backstage/Chart.yaml b/contrib/chart/backstage/Chart.yaml
index 9154368475..21f7084415 100644
--- a/contrib/chart/backstage/Chart.yaml
+++ b/contrib/chart/backstage/Chart.yaml
@@ -5,7 +5,7 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
-version: 0.1.2
+version: 0.1.3
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application.
diff --git a/contrib/chart/backstage/templates/ingress.yaml b/contrib/chart/backstage/templates/ingress.yaml
index 3ec4990561..3fa052485e 100644
--- a/contrib/chart/backstage/templates/ingress.yaml
+++ b/contrib/chart/backstage/templates/ingress.yaml
@@ -3,10 +3,10 @@
{{- $lighthouseUrl := urlParse .Values.appConfig.lighthouse.baseUrl}}
{{/* Determine the api type for the ingress */}}
-{{- if lt .Capabilities.KubeVersion.Minor "19" }}
-apiVersion: networking.k8s.io/v1beta1
-{{- else if ge .Capabilities.KubeVersion.Minor "19" }}
+{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
apiVersion: networking.k8s.io/v1
+{{- else -}}
+apiVersion: networking.k8s.io/v1beta1
{{- end }}
kind: Ingress
metadata:
@@ -29,31 +29,60 @@ spec:
- {{ $frontendUrl.host }}
- {{ $backendUrl.host }}
- {{ $lighthouseUrl.host }}
-
rules:
- host: {{ $frontendUrl.host }}
http:
paths:
- path: /
+ {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
+ pathType: Prefix
+ {{- end }}
backend:
+ {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
+ service:
+ name: {{ include "frontend.serviceName" . }}
+ port:
+ number: 80
+ {{- else -}}
serviceName: {{ include "frontend.serviceName" . }}
servicePort: 80
- {{/* Route the backend inside the same hostname as the frontend when they are the same */}}
- {{- if eq $frontendUrl.host $backendUrl.host}}
+ {{- end }}
+ {{/* Route the backend inside the same hostname as the frontend when they are the same */}}
+ {{- if eq $frontendUrl.host $backendUrl.host}}
- path: /api/
+ {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
+ pathType: Prefix
+ {{- end }}
backend:
+ {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
+ service:
+ name: {{ include "backend.serviceName" . }}
+ port:
+ number: 80
+ {{- else -}}
serviceName: {{ include "backend.serviceName" . }}
servicePort: 80
- {{/* Route the backend through a different host */}}
- {{- else -}}
+ {{- end }}
+ {{/* Route the backend through a different host */}}
+ {{- else -}}
- host: {{ $backendUrl.host }}
http:
paths:
- path: {{ $backendUrl.path | default "/" }}
+ {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
+ pathType: Prefix
+ {{- end }}
backend:
+ {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
+ service:
+ name: {{ include "frontend.serviceName" . }}
+ port:
+ number: 80
+ {{- else -}}
serviceName: {{ include "backend.serviceName" . }}
servicePort: 80
- {{- end }}
+ {{- end }}
+ {{- end }}
{{/* Route lighthouse through a different host */}}
{{- if not ( eq $frontendUrl.host $lighthouseUrl.host ) }}
@@ -61,13 +90,27 @@ spec:
http:
paths:
- path: {{ $lighthouseUrl.path | default "/" }}
+ {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
+ pathType: Prefix
+ {{- end }}
backend:
+ {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
+ service:
+ name: {{ include "lighthouse.serviceName" . }}
+ port:
+ number: 80
+ {{- else -}}
serviceName: {{ include "lighthouse.serviceName" . }}
servicePort: 80
+ {{- end }}
{{- else }}
{{/* Route lighthouse by path with re-write rules when it is hosted under the same hostname */}}
---
+{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
+apiVersion: networking.k8s.io/v1
+{{- else -}}
apiVersion: networking.k8s.io/v1beta1
+{{- end }}
kind: Ingress
metadata:
name: {{ include "backstage.fullname" . }}-ingress-lighthouse
@@ -92,7 +135,17 @@ spec:
http:
paths:
- path: {{$lighthouseUrl.path}}(/|$)(.*)
+ {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
+ pathType: Prefix
+ {{- end }}
backend:
+ {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
+ service:
+ name: {{ include "lighthouse.serviceName" . }}
+ port:
+ number: 80
+ {{- else -}}
serviceName: {{ include "lighthouse.serviceName" . }}
servicePort: 80
+ {{- end }}
{{- end }}
diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md
index 57473ce4c7..a38910df6c 100644
--- a/docs/features/software-catalog/descriptor-format.md
+++ b/docs/features/software-catalog/descriptor-format.md
@@ -727,7 +727,7 @@ filtering templates, and should ideally match the Component
You can find out more about the `parameters` key
[here](../software-templates/writing-templates.md)
-### `spec.steps` [optional]
+### `spec.steps` [required]
You can find out more about the `steps` key
[here](../software-templates/writing-templates.md)
@@ -1266,6 +1266,10 @@ shape, this kind has the following structure.
Exactly equal to `backstage.io/v1alpha1` and `Location`, respectively.
+### `spec` [required]
+
+The `spec` field is required. The minimal spec should be an empty object.
+
### `spec.type` [optional]
The single location type, that's common to the targets specified in the spec. If
diff --git a/docs/getting-started/configure-app-with-plugins.md b/docs/getting-started/configure-app-with-plugins.md
index 746b4bd940..1bfabc335c 100644
--- a/docs/getting-started/configure-app-with-plugins.md
+++ b/docs/getting-started/configure-app-with-plugins.md
@@ -90,3 +90,29 @@ extension to `.icon.svg`. For example:
```ts
import InternalToolIcon from './internal-tool.icon.svg';
```
+
+On mobile devices the `Sidebar` is displayed at the bottom of the screen. For
+customizing the experience you can group `SidebarItems` in a `SidebarGroup`
+(Example 1) or create a `SidebarGroup` with a link (Example 2). All
+`SidebarGroup`s are displayed in the bottom navigation with an icon.
+
+```ts
+// Example 1
+} label="Menu">
+ ...
+
+ ...
+
+```
+
+```ts
+// Example 2
+} to="/search">
+ ...
+
+ ...
+
+```
+
+If no `SidebarGroup` is provided a default menu will display the `Sidebar`
+content.
diff --git a/docs/integrations/aws-s3/discovery.md b/docs/integrations/aws-s3/discovery.md
index d50aceacb8..545a578e63 100644
--- a/docs/integrations/aws-s3/discovery.md
+++ b/docs/integrations/aws-s3/discovery.md
@@ -26,3 +26,16 @@ catalog:
```
Note the `s3-discovery` type, as this is not a regular `url` processor.
+
+As this processor is not one of the default providers, you will also need to add
+the below to `packages/backend/src/plugins/catalog.ts`:
+
+```ts
+/* packages/backend/src/plugins/catalog.ts */
+
+import { AwsS3DiscoveryProcessor } from '@backstage/plugin-catalog-backend';
+
+const builder = await CatalogBuilder.create(env);
+/** ... other processors ... */
+builder.addProcessor(new AwsS3DiscoveryProcessor(env.reader));
+```
diff --git a/docs/local-dev/cli-commands.md b/docs/local-dev/cli-commands.md
index caec60ed70..21b129e0c2 100644
--- a/docs/local-dev/cli-commands.md
+++ b/docs/local-dev/cli-commands.md
@@ -556,6 +556,7 @@ Options:
--package <name> Only load config schema that applies to the given package
--lax Do not require environment variables to be set
--frontend Only validate the frontend configuration
+ --deprecated List all deprecated configuration settings
--config <path> Config files to load instead of app-config.yaml (default: [])
-h, --help display help for command
```
diff --git a/docs/overview/architecture-overview.md b/docs/overview/architecture-overview.md
index d3f171e4f8..5d27cc4184 100644
--- a/docs/overview/architecture-overview.md
+++ b/docs/overview/architecture-overview.md
@@ -202,14 +202,14 @@ with a smaller collection of plugins.
### Plugin Packages
A typical plugin consists of up to five packages, two frontend ones, two
-backend, and one isomorphic packages. All packages within the plugin must share
-a common prefix, typically of the form `@/plugin-`, but
+backend, and one isomorphic package. All packages within the plugin must share a
+common prefix, typically of the form `@/plugin-`, but
alternatives like `backstage-plugin-` or
`@scope/backstage-plugin-` are also valid. Along with this prefix,
each of the packages have their own unique suffix that denotes their role. In
-addition to these five plugin packages it's also possible for to a plugin to
-have additional frontend and backend modules that can be installed to enable
-optional features. For a full list of suffixes and their roles, see the
+addition to these five plugin packages it's also possible for a plugin to have
+additional frontend and backend modules that can be installed to enable optional
+features. For a full list of suffixes and their roles, see the
[Plugin Package Structure ADR](../architecture-decisions/adr011-plugin-package-structure.md).
The `-react`, `-common`, and `-node` plugin packages together form the external
@@ -230,7 +230,7 @@ The frontend packages are grouped into two main groups. The first one is
as well as provide a foundation for the plugin libraries to rely upon.
The second group is the rest of the shared packages, further divided into
-"Frontend Plugin Core" and "Frontend Libraries". The core packages that are
+"Frontend Plugin Core" and "Frontend Libraries". The core packages are
considered particularly stable and form the core of the frontend framework.
Their most important role is to form the boundary around each plugin and provide
a set of tools that helps you combine a collection of plugins into a running
@@ -246,10 +246,10 @@ however likely to change in the future.
### Common Packages
-The common packages are the packages are effectively depended on by all other
-pages. This is a much smaller set of packages but they are also very pervasive.
-Because the common packages are isomorphic and must execute both in the frontend
-and backend, they are never allowed to depend on any of the frontend of backend
+The common packages are the packages effectively depended on by all other pages.
+This is a much smaller set of packages but they are also very pervasive. Because
+the common packages are isomorphic and must execute both in the frontend and
+backend, they are never allowed to depend on any of the frontend of backend
packages.
The Backstage CLI is in a category of its own and is depended on by virtually
diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md
index 4fde1662eb..30338a6ee6 100644
--- a/docs/overview/roadmap.md
+++ b/docs/overview/roadmap.md
@@ -41,8 +41,8 @@ If you have specific questions about the roadmap, please create an
The Backstage roadmap lays out both [“what’s next”](#whats-next) and
[“future work”](#future-work). With "next" we mean features planned for release
-within the ongoing quarter starting in July until September 2021 included. With
-"future" we mean features in the radar, but not yet scheduled.
+within the ongoing quarter from January through March 2022. With "future" we
+mean features in the radar, but not yet scheduled.
The long-term roadmap (12 - 36 months) is not detailed in the public roadmap.
Third-party contributions are also not currently included in the roadmap. Let us
@@ -61,92 +61,57 @@ The feature set below is planned for the ongoing quarter, and grouped by theme.
The list order doesn’t necessarily reflect priority, and the development/release
cycle will vary based on maintainer schedules.
-### Backstage Core
+### Backstage 1.0 (and following versions)
-The following features are planned for release:
+During the first quarter of 2022, we plan to finalize and release version 1.0 of
+the Backstage platform (defined by the Core,
+[Catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview),
+[Scaffolder](https://backstage.io/docs/features/software-templates/software-templates-index)
+and [TechDocs](https://backstage.io/docs/features/techdocs/techdocs-overview)).
+Included as part of this milestone:
-- **Improved responsiveness:** Check out the
- [RFC here](https://github.com/backstage/backstage/issues/6318) for further
- details on how to improve the responsiveness for Backstage's UI.
+- Deciding on the cadence of minor/weekly/daily releases to provide clarity on
+ the frequency and expectations for future versions of the platform and its
+ defining modules.
+- Establish the support model to set the expectations from the adopters in their
+ respective use cases.
-### Software Templates
+### Backstage Security Audit
-The following features are planned for release::
+This initiative is the first of a broader Security Strategy for Backstage. The
+purpose of the Security Audit is to involve third-party companies in auditing
+the platform and highlighting potential vulnerabilities. The benefit for the
+adopters is clear: We want Backstage to be as secure as possible, and we want to
+make it reliable through a specific initiative. This initiative in particular is
+done together, and with the support of, the
+[Cloud Native Computing Foundation (CNCF)](https://www.cncf.io/).
-- **Re-creation/resubmission in case of failure:** Speed up productivity by
- allowing developers to relaunch a project after a failure or any unexpected
- problem. In the current version, this task requires retyping and a full
- re-creation from scratch.
-- **Performance and usability improvements for contributors:** Reach a relevant
- improvement in templating's performance through the replacement of
- [handlebars](https://handlebarsjs.com/). Other replacements will be considered
- as part of this task (possibly
- [cookiecutter](https://cookiecutter.readthedocs.io/)) for easier software
- template creation, allowing more contributors to reach their goals without
- having to learn new tooling.
-- **Improved extensibility through inclusion:** Make software templates more
- maintainable and extensible by adding `$include` support for parameters.
-- **Authenticated job creation:** Created jobs will be able to run with an
- authenticated user with all actions tracked for future consumption and
- evidence. Track users creating jobs and make “jobs created by me” reporting
- available.
+### Moving to Incubation in CNCF
-### Software Catalog
-
-The following features are planned for release:
-
-- **Request For Comments (RFC) for composability improvements (routing):**
- Enable plugins to be auto-added and make plugin installation and upgrades
- easier for all Backstage users. This includes information card layouts, entity
- pages containing content and hooking the external header, considering the
- support of a separate deployment, and configuration for plugins.
-- **Removing duplicated entities in catalog:** As any adopter knows, a software
- catalog can contain thousands or more entities and it is very important to
- avoid duplications in naming to prevent failures. With this development task,
- two entities with the same name won't be allowed as described
- [here](https://github.com/backstage/backstage/issues/4760).
-- **Connecting identity to ownership to prepare for role-based access control
- ([RBAC](https://en.wikipedia.org/wiki/Role-based_access_control)):** This is a
- first step to supporting RBAC for the software catalog (see the
- [future work section](#future-work) for further details). Provide each entity
- within the software catalog with a recognized owner.
-- **Catalog performance improvements through improved caching:** Fix the
- performance gaps in the catalog processor, which currently doesn’t have a
- strong caching mechanism. The current version often requires fetching a
- relevant amount of data, especially at scale.
+The progress of the request can be seen
+[here](https://github.com/cncf/toc/pull/717).
## Future work
-The following feature list doesn’t represent a commitment to develop and the
-list order doesn’t reflect any priority or importance. But these features are on
+The following feature list doesn’t represent a commitment to develop, and the
+list order doesn’t reflect any priority or importance, but these features are on
the maintainers’ radar, with clear interest expressed by the community.
+- **Backend Services:** To better scale and maintain the Backstage instances, a
+ backend layer of services is planned to be introduced as part of the software
+ architecture. This layer of backend services will help in decoupling the
+ various modules (e.g. Catalog and Scaffolder) from the frontend experience.
+- **Security Plan (and Strategy):** The purpose of the Security Strategy is to
+ move another step along the path to maturing the platform, setting the
+ expectations of any adopters from a security standpoint.
+- **Search GA:**.
+- **[GraphQL](https://graphql.org/) support:** Introduce the ability to query
+ Backstage backend services with a standard query language for APIs.
+- **Telemetry:** To efficiently generate logging and metrics in such a way that
+ adopters can get insights so that Backstage can be monitored and improved.
- **Improved UX design:** Provide a better Backstage user experience through
visual guidelines and templates, especially navigation across plug-ins and
portal functionalities.
-- **Catalog composability (routing):** Follow up development after the RFC
- planned for the ongoing quarter (see [what’s next](#whats-next) for further
- details).
-- **Catalog-import improvements:** Provide a faster (scalability) and better
- (more features like move/rename) way to import entities into the Software
- Catalog. Importing items in the Software Catalog is crucial for creating a
- Backstage proof-of-concept or testing/planning for broader organizational
- adoption. This enhancement better supports getting developers to use Backstage
- with less effort and customization.
-- **Catalog improvements:** Add pagination and sourcing to Software Catalog.
-- **[GraphQL](https://graphql.org/) support:** Introduce the ability to query
- Backstage backend services with a standard query language for APIs.
-- **Software templates performance improvements through decoupling a separate
- worker:** Improve performance through decoupling resource-consuming services
- and making them asynchronous. In the current version, project auto-creation
- through the Software Templating system can consume a lot of resources and
- bottleneck many concurrent projects created simultaneously.
-- **API discovery and documentation:** Add better support for the
- [gRPC](https://grpc.io/).
-- **TechDocs GA release:** Work toward enhancements necessary to get TechDocs to
- general availability. Check out the
- [milestone here](https://github.com/backstage/backstage/milestone/30) for
- further details.
## Completed milestones
@@ -177,3 +142,19 @@ Read more about the completed (and released) features for reference.
- Support auth providers: Google, Okta, GitHub, GitLab,
[auth0](https://github.com/backstage/backstage/pull/1611),
[AWS](https://github.com/backstage/backstage/pull/1990)
+
+- [Donate Backstage to the CNCF 🎉](https://backstage.io/blog/2020/09/23/backstage-cncf-sandbox)
+- [TechDocs v1](https://backstage.io/blog/2020/09/08/announcing-tech-docs)
+- [Plugin marketplace](https://backstage.io/plugins)
+- [Improved and move documentation to backstage.io](https://backstage.io/docs/overview/what-is-backstage)
+- [Backstage Software Catalog (alpha)](https://backstage.io/blog/2020/06/22/backstage-service-catalog-alpha)
+- [Backstage Software Templates (beta)](https://backstage.io/blog/2021/07/26/software-templates-are-now-in-beta)
+- [Make it possible to add custom auth providers](https://backstage.io/blog/2020/07/01/how-to-enable-authentication-in-backstage-using-passport)
+- [TechDocs v0](https://github.com/backstage/backstage/milestone/15)
+- CI plugins: CircleCI, Jenkins, GitHub Actions and TravisCI
+- [Service API documentation](https://github.com/backstage/backstage/pull/1737)
+- Backstage Software Catalog can read from: GitHub, GitLab,
+ [Bitbucket](https://github.com/backstage/backstage/pull/1938)
+- Support auth providers: Google, Okta, GitHub, GitLab,
+ [auth0](https://github.com/backstage/backstage/pull/1611),
+ [AWS](https://github.com/backstage/backstage/pull/1990)
diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md
index 05af512f92..de4bc15cf1 100644
--- a/docs/plugins/composability.md
+++ b/docs/plugins/composability.md
@@ -522,157 +522,3 @@ clarify intent. Refer to the following table to formulate the new name:
| Entity Overview Card | \*Card | Entity\*Card | EntitySentryCard, EntityPagerDutyCard |
| Entity Conditional | isPluginApplicableToEntity | is\*Available | isPagerDutyAvailable, isJenkinsAvailable |
| Plugin Instance | plugin | \*Plugin | jenkinsPlugin, catalogPlugin |
-
-## Porting Existing Apps
-
-The first step of porting any app is to replace the root `Routes` component with
-`FlatRoutes` from `@backstage/core-app-api`. As opposed to the `Routes`
-component, `FlatRoutes` only considers the first level of `Route` components in
-its children, and provides any additional children to the outlet of the route.
-It also removes the need to append `"/*"` to paths, as it is added
-automatically.
-
-```diff
-const AppRoutes = () => (
--
-+
- ...
-- } />
-+ } />
- ...
--
-+
-);
-```
-
-The next step should be to switch from using `EntityPageLayout` to
-`EntityLayout`, as this can also be done without waiting for plugins to be
-ported. You should also replace the top-level `Router` from the catalog plugin
-with the separate `CatalogIndexPage` and `CatalogEntityPage` extensions that
-have been added to the catalog:
-
-```diff
--}
--/>
-+} />
-+}
-+>
-+
-+
-```
-
-At that point you should flatten out the element tree as much as possible in the
-app, removing any intermediate components. At the top level this should usually
-be straightforward, but when reaching the catalog entity pages you may need to
-wait for some plugins to be migrated. This is because it is no longer possible
-to pass in the selected entity through component props, and it should be picked
-up from context inside the plugin instead. See the sections below for how to
-carry out migrations of some common entity page patterns.
-
-Once the app element tree doesn't contain any intermediate components, and all
-plugin imports have been switched to extensions rather than plain components,
-the app has been fully ported.
-
-### Switching from EntityPageLayout to EntityLayout
-
-The existing `EntityPageLayout` is replaced by the new `EntityLayout` component,
-which has a slightly different pattern for expressing the contents and paths.
-
-Porting from the old to the new API is just a matter of moving some things
-around. For example, given the following existing code:
-
-```tsx
-
- }
- />
- }
- />
- }
- />
-
-```
-
-It would be ported to this:
-
-```tsx
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-In addition to the renaming, the `element` prop has been moved to `children`.
-Also note that the `/*` suffix has been removed from the `"/kubernetes"` path,
-as it's now added automatically.
-
-Usage of the `EntityLayout` component is required to be able to properly
-discover routes, and so it is required to apply this change before you can start
-using routable entity content extensions from plugins.
-
-### Porting Entity Pages
-
-The established pattern in the app is to use custom components in order to
-select what plugin components to render for a given entity. The new
-`EntitySwitch` component introduced above is what is intended to replace this
-pattern, now that the entire app needs to be rendered as a single element tree.
-For example, given the following existing code:
-
-```tsx
-export const EntityPage = () => {
- const { entity } = useEntity();
-
- switch (entity?.kind?.toLowerCase()) {
- case 'component':
- return ;
- case 'api':
- return ;
- case 'group':
- return ;
- case 'user':
- return ;
- default:
- return ;
- }
-};
-```
-
-It would be migrated to this:
-
-```tsx
-export const entityPage = (
-
-
-
-
-
-
-
-);
-```
-
-Note that for example `` has been changed to simply
-`componentPage`, that is because just like the `EntityPage` component, the
-`ComponentEntityPage` also needs to be ported to be an element rather a
-component in a similar way.
diff --git a/microsite/data/plugins/gocd.yaml b/microsite/data/plugins/gocd.yaml
index 7bf24d75fd..7c7190effc 100644
--- a/microsite/data/plugins/gocd.yaml
+++ b/microsite/data/plugins/gocd.yaml
@@ -5,7 +5,7 @@ authorUrl: https://github.com/soundcloud
category: CI/CD
description: GoCD is an open-source tool which is used in software development to help teams and organizations automate the continuous delivery of software.
documentation: https://github.com/backstage/backstage/tree/master/plugins/gocd
-iconUrl: https://pics.freeicons.io/uploads/icons/png/13646383971540553613-512.png
+iconUrl: https://www.gocd.org/assets/images/go_logo-5b5ca9e1.svg
npmPackageName: '@backstage/plugin-gocd'
tags:
- ci
diff --git a/microsite/yarn.lock b/microsite/yarn.lock
index b4d97d549d..01226a9bf9 100644
--- a/microsite/yarn.lock
+++ b/microsite/yarn.lock
@@ -1197,9 +1197,9 @@ babylon@^6.18.0:
integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
balanced-match@^1.0.0:
- version "1.0.0"
- resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
- integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+ version "1.0.2"
+ resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base64-js@^1.3.1:
version "1.5.1"
@@ -3028,7 +3028,19 @@ glob-to-regexp@^0.3.0:
resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
-glob@^7.0.0, glob@^7.0.5, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@^7.1.7, glob@~7.1.1:
+glob@^7.0.0, glob@^7.0.5, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@^7.1.7:
+ version "7.2.0"
+ resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
+ integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+glob@~7.1.1:
version "7.1.7"
resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
@@ -3556,6 +3568,13 @@ is-core-module@^2.1.0:
dependencies:
has "^1.0.3"
+is-core-module@^2.8.0:
+ version "2.8.1"
+ resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211"
+ integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==
+ dependencies:
+ has "^1.0.3"
+
is-data-descriptor@^0.1.4:
version "0.1.4"
resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
@@ -4793,7 +4812,7 @@ path-key@^2.0.0, path-key@^2.0.1:
resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
-path-parse@^1.0.6:
+path-parse@^1.0.6, path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
@@ -5586,7 +5605,16 @@ resolve-url@^0.2.1:
resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
-resolve@^1.1.6, resolve@^1.10.0:
+resolve@^1.1.6:
+ version "1.21.0"
+ resolved "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f"
+ integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==
+ dependencies:
+ is-core-module "^2.8.0"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
+resolve@^1.10.0:
version "1.19.0"
resolved "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c"
integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==
@@ -5781,9 +5809,9 @@ shell-quote@1.7.2:
integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==
shelljs@^0.8.4:
- version "0.8.4"
- resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2"
- integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==
+ version "0.8.5"
+ resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c"
+ integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==
dependencies:
glob "^7.0.0"
interpret "^1.0.0"
@@ -6133,6 +6161,11 @@ supports-color@^7.1.0:
dependencies:
has-flag "^4.0.0"
+supports-preserve-symlinks-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+ integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
svgo@^1.0.0, svgo@^1.3.2:
version "1.3.2"
resolved "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
diff --git a/packages/app/package.json b/packages/app/package.json
index bc30713a94..8773e441a1 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -57,6 +57,7 @@
"@roadiehq/backstage-plugin-github-insights": "^1.4.2",
"@roadiehq/backstage-plugin-github-pull-requests": "^1.3.2",
"@roadiehq/backstage-plugin-travis-ci": "^1.3.2",
+ "@roadiehq/backstage-plugin-buildkite": "^1.3.4",
"history": "^5.0.0",
"prop-types": "^15.7.2",
"react": "^16.13.1",
diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx
index 2d9b9b97f1..57f1fa536f 100644
--- a/packages/app/src/components/Root/Root.tsx
+++ b/packages/app/src/components/Root/Root.tsx
@@ -23,12 +23,17 @@ import MapIcon from '@material-ui/icons/MyLocation';
import LayersIcon from '@material-ui/icons/Layers';
import LibraryBooks from '@material-ui/icons/LibraryBooks';
import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
+import SearchIcon from '@material-ui/icons/Search';
+import MenuIcon from '@material-ui/icons/Menu';
import MoneyIcon from '@material-ui/icons/MonetizationOn';
import LogoFull from './LogoFull';
import LogoIcon from './LogoIcon';
import { NavLink } from 'react-router-dom';
import { GraphiQLIcon } from '@backstage/plugin-graphiql';
-import { Settings as SidebarSettings } from '@backstage/plugin-user-settings';
+import {
+ Settings as SidebarSettings,
+ UserSettingsSignInAvatar,
+} from '@backstage/plugin-user-settings';
import {
SidebarSearchModal,
SearchContextProvider,
@@ -36,15 +41,15 @@ import {
import { Shortcuts } from '@backstage/plugin-shortcuts';
import {
Sidebar,
- SidebarPage,
sidebarConfig,
SidebarContext,
- SidebarItem,
SidebarDivider,
- SidebarSpace,
+ SidebarGroup,
+ SidebarItem,
+ SidebarPage,
SidebarScrollWrapper,
+ SidebarSpace,
} from '@backstage/core-components';
-import { AzurePullRequestsIcon } from '@backstage/plugin-azure-devops';
const useSidebarLogoStyles = makeStyles({
root: {
@@ -83,34 +88,43 @@ export const Root = ({ children }: PropsWithChildren<{}>) => (
-
-
-
+ } to="/search">
+
+
+
+
- {/* Global nav, not org-specific */}
-
-
-
-
-
- {/* End global nav */}
-
-
-
-
-
-
-
-
-
-
+ }>
+ {/* Global nav, not org-specific */}
+
+
+
+
+
+ {/* End global nav */}
+
+
+
+
+
+
+
+
+
+
-
+ }
+ to="/settings"
+ >
+
+
{children}
diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx
index 60dc0dc979..8af61062a3 100644
--- a/packages/app/src/components/catalog/EntityPage.tsx
+++ b/packages/app/src/components/catalog/EntityPage.tsx
@@ -124,6 +124,10 @@ import {
EntityTravisCIOverviewCard,
isTravisciAvailable,
} from '@roadiehq/backstage-plugin-travis-ci';
+import {
+ EntityBuildkiteContent,
+ isBuildkiteAvailable,
+} from '@roadiehq/backstage-plugin-buildkite';
import {
isNewRelicDashboardAvailable,
EntityNewRelicDashboardContent,
@@ -174,6 +178,10 @@ export const cicdContent = (
+
+
+
+
diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx
index c99bb78c84..9a8c208103 100644
--- a/packages/app/src/components/search/SearchPage.tsx
+++ b/packages/app/src/components/search/SearchPage.tsx
@@ -21,6 +21,7 @@ import {
Header,
Lifecycle,
Page,
+ SidebarPinStateContext,
} from '@backstage/core-components';
import { CatalogResultListItem } from '@backstage/plugin-catalog';
import {
@@ -33,7 +34,7 @@ import {
} from '@backstage/plugin-search';
import { DocsResultListItem } from '@backstage/plugin-techdocs';
import { Grid, List, makeStyles, Paper, Theme } from '@material-ui/core';
-import React from 'react';
+import React, { useContext } from 'react';
const useStyles = makeStyles((theme: Theme) => ({
bar: {
@@ -52,9 +53,11 @@ const useStyles = makeStyles((theme: Theme) => ({
const SearchPage = () => {
const classes = useStyles();
+ const { isMobile } = useContext(SidebarPinStateContext);
+
return (
- } />
+ {!isMobile && } />}
@@ -62,37 +65,39 @@ const SearchPage = () => {
-
- ,
- },
- {
- value: 'techdocs',
- name: 'Documentation',
- icon: ,
- },
- ]}
- />
-
-
+ ,
+ },
+ {
+ value: 'techdocs',
+ name: 'Documentation',
+ icon: ,
+ },
+ ]}
/>
-
-
-
-
+
+
+
+
+
+ )}
+
{({ results }) => (
diff --git a/packages/backend-common/src/config.ts b/packages/backend-common/src/config.ts
index 12cc2e3a9d..9081b99284 100644
--- a/packages/backend-common/src/config.ts
+++ b/packages/backend-common/src/config.ts
@@ -39,7 +39,10 @@ const updateRedactionList = (
configs: AppConfig[],
logger: Logger,
) => {
- const secretAppConfigs = schema.process(configs, { visibility: ['secret'] });
+ const secretAppConfigs = schema.process(configs, {
+ visibility: ['secret'],
+ withDeprecatedKeys: true,
+ });
const secretConfig = ConfigReader.fromConfigs(secretAppConfigs);
const values = new Set();
const data = secretConfig.get();
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 29f9059593..d11fd64441 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -32,6 +32,7 @@
"@backstage/integration": "^0.7.1",
"@backstage/plugin-app-backend": "^0.3.21",
"@backstage/plugin-auth-backend": "^0.6.2",
+ "@backstage/plugin-azure-devops-backend": "^0.3.0",
"@backstage/plugin-badges-backend": "^0.1.15",
"@backstage/plugin-catalog-backend": "^0.20.0",
"@backstage/plugin-code-coverage-backend": "^0.1.19",
diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts
index b765df38f6..1942c36ad1 100644
--- a/packages/backend/src/index.ts
+++ b/packages/backend/src/index.ts
@@ -40,6 +40,7 @@ import { Config } from '@backstage/config';
import healthcheck from './plugins/healthcheck';
import { metricsInit, metricsHandler } from './metrics';
import auth from './plugins/auth';
+import azureDevOps from './plugins/azure-devops';
import catalog from './plugins/catalog';
import codeCoverage from './plugins/codecoverage';
import kubernetes from './plugins/kubernetes';
@@ -116,6 +117,7 @@ async function main() {
);
const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder'));
const authEnv = useHotMemoize(module, () => createEnv('auth'));
+ const azureDevOpsEnv = useHotMemoize(module, () => createEnv('azure-devops'));
const proxyEnv = useHotMemoize(module, () => createEnv('proxy'));
const rollbarEnv = useHotMemoize(module, () => createEnv('rollbar'));
const searchEnv = useHotMemoize(module, () => createEnv('search'));
@@ -139,6 +141,7 @@ async function main() {
apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv));
apiRouter.use('/tech-insights', await techInsights(techInsightsEnv));
apiRouter.use('/auth', await auth(authEnv));
+ apiRouter.use('/azure-devops', await azureDevOps(azureDevOpsEnv));
apiRouter.use('/search', await search(searchEnv));
apiRouter.use('/techdocs', await techdocs(techdocsEnv));
apiRouter.use('/todo', await todo(todoEnv));
diff --git a/packages/backend/src/plugins/azure-devops.ts b/packages/backend/src/plugins/azure-devops.ts
new file mode 100644
index 0000000000..4120e655ed
--- /dev/null
+++ b/packages/backend/src/plugins/azure-devops.ts
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2020 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { createRouter } from '@backstage/plugin-azure-devops-backend';
+import { Router } from 'express';
+import type { PluginEnvironment } from '../types';
+
+export default function createPlugin({
+ logger,
+ config,
+}: PluginEnvironment): Promise {
+ return createRouter({ logger, config });
+}
diff --git a/packages/cli/package.json b/packages/cli/package.json
index edb2cfb3ce..95cb4f70de 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -45,8 +45,8 @@
"@spotify/eslint-config-typescript": "^12.0.0",
"@sucrase/jest-plugin": "^2.1.1",
"@sucrase/webpack-loader": "^2.0.0",
- "@svgr/plugin-jsx": "5.5.x",
- "@svgr/plugin-svgo": "5.4.x",
+ "@svgr/plugin-jsx": "6.2.x",
+ "@svgr/plugin-svgo": "6.2.x",
"@svgr/rollup": "5.5.x",
"@svgr/webpack": "5.5.x",
"@types/webpack-env": "^1.15.2",
diff --git a/packages/cli/src/commands/config/validate.ts b/packages/cli/src/commands/config/validate.ts
index 2d3ce60366..8d528b8300 100644
--- a/packages/cli/src/commands/config/validate.ts
+++ b/packages/cli/src/commands/config/validate.ts
@@ -23,5 +23,6 @@ export default async (cmd: Command) => {
fromPackage: cmd.package,
mockEnv: cmd.lax,
fullVisibility: !cmd.frontend,
+ withDeprecatedKeys: cmd.deprecated,
});
};
diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts
index fa00bf4073..dfe427b27b 100644
--- a/packages/cli/src/commands/index.ts
+++ b/packages/cli/src/commands/index.ts
@@ -193,6 +193,7 @@ export function registerCommands(program: CommanderStatic) {
)
.option('--lax', 'Do not require environment variables to be set')
.option('--frontend', 'Only validate the frontend configuration')
+ .option('--deprecated', 'Output deprecated configuration settings')
.option(...configOption)
.description(
'Validate that the given configuration loads and matches schema',
diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts
index b43e4798b4..e7ceeadf08 100644
--- a/packages/cli/src/lib/config.ts
+++ b/packages/cli/src/lib/config.ts
@@ -30,6 +30,7 @@ type Options = {
fromPackage?: string;
mockEnv?: boolean;
withFilteredKeys?: boolean;
+ withDeprecatedKeys?: boolean;
fullVisibility?: boolean;
};
@@ -91,6 +92,7 @@ export async function loadCliConfig(options: Options) {
? ['frontend', 'backend', 'secret']
: ['frontend'],
withFilteredKeys: options.withFilteredKeys,
+ withDeprecatedKeys: options.withDeprecatedKeys,
});
const frontendConfig = ConfigReader.fromConfigs(frontendAppConfigs);
diff --git a/packages/config-loader/api-report.md b/packages/config-loader/api-report.md
index 106c744d7b..06868e0f47 100644
--- a/packages/config-loader/api-report.md
+++ b/packages/config-loader/api-report.md
@@ -21,6 +21,7 @@ export type ConfigSchemaProcessingOptions = {
visibility?: ConfigVisibility[];
valueTransform?: TransformFunc;
withFilteredKeys?: boolean;
+ withDeprecatedKeys?: boolean;
};
// @public (undocumented)
diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts
index 8e7f582e44..a05796139b 100644
--- a/packages/config-loader/src/lib/schema/collect.ts
+++ b/packages/config-loader/src/lib/schema/collect.ts
@@ -182,10 +182,10 @@ function compileTsSchemas(paths: string[]) {
program,
// All schemas should export a `Config` symbol
'Config',
- // This enables usage of @visibility is doc comments
+ // This enables usage of @visibility and @deprecated in doc comments
{
required: true,
- validationKeywords: ['visibility'],
+ validationKeywords: ['visibility', 'deprecated'],
},
[path.split(sep).join('/')], // Unix paths are expected for all OSes here
) as JsonObject | null;
diff --git a/packages/config-loader/src/lib/schema/compile.test.ts b/packages/config-loader/src/lib/schema/compile.test.ts
index 1e63f3cbf8..7b7db87c8f 100644
--- a/packages/config-loader/src/lib/schema/compile.test.ts
+++ b/packages/config-loader/src/lib/schema/compile.test.ts
@@ -40,6 +40,7 @@ describe('compileConfigSchemas', () => {
],
visibilityByDataPath: new Map(),
visibilityBySchemaPath: new Map(),
+ deprecationByDataPath: new Map(),
});
expect(validate([{ data: { b: 'b' }, context: 'test' }])).toEqual({
errors: [
@@ -53,6 +54,7 @@ describe('compileConfigSchemas', () => {
],
visibilityByDataPath: new Map(),
visibilityBySchemaPath: new Map(),
+ deprecationByDataPath: new Map(),
});
});
@@ -112,6 +114,7 @@ describe('compileConfigSchemas', () => {
'/properties/d/items': 'frontend',
}),
),
+ deprecationByDataPath: new Map(),
});
});
@@ -137,4 +140,34 @@ describe('compileConfigSchemas', () => {
"Config schema visibility is both 'frontend' and 'secret' for properties/a/visibility",
);
});
+
+ it('should discover deprecations', () => {
+ const validate = compileConfigSchemas([
+ {
+ path: 'a1',
+ value: {
+ type: 'object',
+ properties: {
+ a: { type: 'string', deprecated: 'deprecation reason for a' },
+ b: { type: 'string', deprecated: 'deprecation reason for b' },
+ c: { type: 'string' },
+ },
+ },
+ },
+ ]);
+ expect(
+ validate([
+ { data: { a: 'a', b: 'b', c: 'c', d: ['d'] }, context: 'test' },
+ ]),
+ ).toEqual({
+ deprecationByDataPath: new Map(
+ Object.entries({
+ '/a': 'deprecation reason for a',
+ '/b': 'deprecation reason for b',
+ }),
+ ),
+ visibilityByDataPath: new Map(),
+ visibilityBySchemaPath: new Map(),
+ });
+ });
});
diff --git a/packages/config-loader/src/lib/schema/compile.ts b/packages/config-loader/src/lib/schema/compile.ts
index 1cbf5a404d..b7f705751e 100644
--- a/packages/config-loader/src/lib/schema/compile.ts
+++ b/packages/config-loader/src/lib/schema/compile.ts
@@ -40,6 +40,7 @@ export function compileConfigSchemas(
// output during validation. We work around this by having this extra piece
// of state that we reset before each validation.
const visibilityByDataPath = new Map();
+ const deprecationByDataPath = new Map();
const ajv = new Ajv({
allErrors: true,
@@ -47,28 +48,48 @@ export function compileConfigSchemas(
schemas: {
'https://backstage.io/schema/config-v1': true,
},
- }).addKeyword({
- keyword: 'visibility',
- metaSchema: {
- type: 'string',
- enum: CONFIG_VISIBILITIES,
- },
- compile(visibility: ConfigVisibility) {
- return (_data, context) => {
- if (context?.dataPath === undefined) {
- return false;
- }
- if (visibility && visibility !== 'backend') {
+ })
+ .addKeyword({
+ keyword: 'visibility',
+ metaSchema: {
+ type: 'string',
+ enum: CONFIG_VISIBILITIES,
+ },
+ compile(visibility: ConfigVisibility) {
+ return (_data, context) => {
+ if (context?.dataPath === undefined) {
+ return false;
+ }
+ if (visibility && visibility !== 'backend') {
+ const normalizedPath = context.dataPath.replace(
+ /\['?(.*?)'?\]/g,
+ (_, segment) => `/${segment}`,
+ );
+ visibilityByDataPath.set(normalizedPath, visibility);
+ }
+ return true;
+ };
+ },
+ })
+ .removeKeyword('deprecated') // remove `deprecated` keyword so that we can implement our own compiler
+ .addKeyword({
+ keyword: 'deprecated',
+ metaSchema: { type: 'string' },
+ compile(deprecationDescription: string) {
+ return (_data, context) => {
+ if (context?.dataPath === undefined) {
+ return false;
+ }
const normalizedPath = context.dataPath.replace(
/\['?(.*?)'?\]/g,
(_, segment) => `/${segment}`,
);
- visibilityByDataPath.set(normalizedPath, visibility);
- }
- return true;
- };
- },
- });
+ // create mapping of deprecation description and data path of property
+ deprecationByDataPath.set(normalizedPath, deprecationDescription);
+ return true;
+ };
+ },
+ });
for (const schema of schemas) {
try {
@@ -79,6 +100,7 @@ export function compileConfigSchemas(
}
const merged = mergeConfigSchemas(schemas.map(_ => _.value));
+
const validate = ajv.compile(merged);
const visibilityBySchemaPath = new Map();
@@ -94,17 +116,20 @@ export function compileConfigSchemas(
visibilityByDataPath.clear();
const valid = validate(config);
+
if (!valid) {
return {
errors: validate.errors ?? [],
visibilityByDataPath: new Map(visibilityByDataPath),
visibilityBySchemaPath,
+ deprecationByDataPath,
};
}
return {
visibilityByDataPath: new Map(visibilityByDataPath),
visibilityBySchemaPath,
+ deprecationByDataPath,
};
};
}
diff --git a/packages/config-loader/src/lib/schema/filtering.test.ts b/packages/config-loader/src/lib/schema/filtering.test.ts
index be07b915ed..df06975c9f 100644
--- a/packages/config-loader/src/lib/schema/filtering.test.ts
+++ b/packages/config-loader/src/lib/schema/filtering.test.ts
@@ -64,6 +64,13 @@ const visibility = new Map(
}),
);
+const deprecations = new Map(
+ Object.entries({
+ '/arr': 'deprecated array',
+ '/objB/never': 'deprecated nested property',
+ }),
+);
+
describe('filterByVisibility', () => {
test.each<[ConfigVisibility[], JsonObject]>([
[
@@ -185,9 +192,34 @@ describe('filterByVisibility', () => {
[['frontend', 'backend', 'secret'], { data, filteredKeys: [] }],
])('should filter correctly with %p', (filter, expected) => {
expect(
- filterByVisibility(data, filter, visibility, undefined, true),
+ filterByVisibility(
+ data,
+ filter,
+ visibility,
+ deprecations,
+ undefined,
+ true,
+ false,
+ ),
).toEqual(expected);
});
+
+ it('should include deprecated keys regardless of visibility', () => {
+ expect(
+ filterByVisibility(
+ data,
+ [],
+ visibility,
+ deprecations,
+ undefined,
+ true,
+ true,
+ ).deprecatedKeys,
+ ).toEqual([
+ { key: 'arr', description: 'deprecated array' },
+ { key: 'objB.never', description: 'deprecated nested property' },
+ ]);
+ });
});
describe('filterErrorsByVisibility', () => {
diff --git a/packages/config-loader/src/lib/schema/filtering.ts b/packages/config-loader/src/lib/schema/filtering.ts
index b2f39d3cc4..2855979b1f 100644
--- a/packages/config-loader/src/lib/schema/filtering.ts
+++ b/packages/config-loader/src/lib/schema/filtering.ts
@@ -30,10 +30,17 @@ export function filterByVisibility(
data: JsonObject,
includeVisibilities: ConfigVisibility[],
visibilityByDataPath: Map,
+ deprecationByDataPath: Map,
transformFunc?: TransformFunc,
withFilteredKeys?: boolean,
-): { data: JsonObject; filteredKeys?: string[] } {
+ withDeprecatedKeys?: boolean,
+): {
+ data: JsonObject;
+ filteredKeys?: string[];
+ deprecatedKeys?: { key: string; description: string }[];
+} {
const filteredKeys = new Array();
+ const deprecatedKeys = new Array<{ key: string; description: string }>();
function transform(
jsonVal: JsonValue,
@@ -44,6 +51,12 @@ export function filterByVisibility(
visibilityByDataPath.get(visibilityPath) ?? DEFAULT_CONFIG_VISIBILITY;
const isVisible = includeVisibilities.includes(visibility);
+ // deprecated keys are added regardless of visibility indicator
+ const deprecation = deprecationByDataPath.get(visibilityPath);
+ if (deprecation) {
+ deprecatedKeys.push({ key: filterPath, description: deprecation });
+ }
+
if (typeof jsonVal !== 'object') {
if (isVisible) {
if (transformFunc) {
@@ -109,6 +122,7 @@ export function filterByVisibility(
return {
filteredKeys: withFilteredKeys ? filteredKeys : undefined,
+ deprecatedKeys: withDeprecatedKeys ? deprecatedKeys : undefined,
data: (transform(data, '', '') as JsonObject) ?? {},
};
}
diff --git a/packages/config-loader/src/lib/schema/load.test.ts b/packages/config-loader/src/lib/schema/load.test.ts
index e2ae3cc2c7..8510ccb1d8 100644
--- a/packages/config-loader/src/lib/schema/load.test.ts
+++ b/packages/config-loader/src/lib/schema/load.test.ts
@@ -62,7 +62,10 @@ describe('loadConfigSchema', () => {
expect(schema.process(configs)).toEqual(configs);
expect(schema.process(configs, { visibility: ['frontend'] })).toEqual([
- { data: { key1: 'a' }, context: 'test' },
+ {
+ data: { key1: 'a' },
+ context: 'test',
+ },
]);
expect(
schema.process(configs, {
@@ -71,7 +74,11 @@ describe('loadConfigSchema', () => {
withFilteredKeys: true,
}),
).toEqual([
- { data: { key1: 'X' }, context: 'test', filteredKeys: ['key2'] },
+ {
+ data: { key1: 'X' },
+ context: 'test',
+ filteredKeys: ['key2'],
+ },
]);
expect(
schema.process(configs, {
@@ -79,14 +86,21 @@ describe('loadConfigSchema', () => {
withFilteredKeys: true,
}),
).toEqual([
- { data: { key1: 'X', key2: 'X' }, context: 'test', filteredKeys: [] },
+ {
+ data: { key1: 'X', key2: 'X' },
+ context: 'test',
+ filteredKeys: [],
+ },
]);
const serialized = schema.serialize();
const schema2 = await loadConfigSchema({ serialized });
expect(schema2.process(configs, { visibility: ['frontend'] })).toEqual([
- { data: { key1: 'a' }, context: 'test' },
+ {
+ data: { key1: 'a' },
+ context: 'test',
+ },
]);
expect(() =>
schema2.process([...configs, { data: { key1: 3 }, context: 'test2' }]),
@@ -131,7 +145,10 @@ describe('loadConfigSchema', () => {
'Config validation failed, Config should be number { type=number } at /key2',
);
expect(schema.process(configs, { visibility: ['frontend'] })).toEqual([
- { data: { key1: 'a' }, context: 'test' },
+ {
+ data: { key1: 'a' },
+ context: 'test',
+ },
]);
expect(() => schema.process(configs, { visibility: ['secret'] })).toThrow(
'Config validation failed, Config should be number { type=number } at /key2',
@@ -179,7 +196,12 @@ describe('loadConfigSchema', () => {
];
expect(
schema.process(mkConfig({ x: 1 }), { visibility: ['frontend'] }),
- ).toEqual([{ data: { nested: [{}] }, context: 'test' }]);
+ ).toEqual([
+ {
+ data: { nested: [{}] },
+ context: 'test',
+ },
+ ]);
expect(() => schema.process(mkConfig({ y: 1 }))).toThrow(
'Config validation failed, Config should be string { type=string } at /nested/0/y',
);
@@ -190,10 +212,20 @@ describe('loadConfigSchema', () => {
);
expect(
schema.process(mkConfig({ x: 'a' }), { visibility: ['frontend'] }),
- ).toEqual([{ data: { nested: [{}] }, context: 'test' }]);
+ ).toEqual([
+ {
+ data: { nested: [{}] },
+ context: 'test',
+ },
+ ]);
expect(
schema.process(mkConfig({ y: 'aaa' }), { visibility: ['frontend'] }),
- ).toEqual([{ data: { nested: [{ y: 'aaa' }] }, context: 'test' }]);
+ ).toEqual([
+ {
+ data: { nested: [{ y: 'aaa' }] },
+ context: 'test',
+ },
+ ]);
expect(() =>
schema.process(mkConfig({ y: 'aaaa' }), { visibility: ['frontend'] }),
).toThrow(
diff --git a/packages/config-loader/src/lib/schema/load.ts b/packages/config-loader/src/lib/schema/load.ts
index 2d78785304..aa7ed06305 100644
--- a/packages/config-loader/src/lib/schema/load.ts
+++ b/packages/config-loader/src/lib/schema/load.ts
@@ -82,7 +82,7 @@ export async function loadConfigSchema(
return {
process(
configs: AppConfig[],
- { visibility, valueTransform, withFilteredKeys } = {},
+ { visibility, valueTransform, withFilteredKeys, withDeprecatedKeys } = {},
): AppConfig[] {
const result = validate(configs);
@@ -105,8 +105,10 @@ export async function loadConfigSchema(
data,
visibility,
result.visibilityByDataPath,
+ result.deprecationByDataPath,
valueTransform,
withFilteredKeys,
+ withDeprecatedKeys,
),
}));
} else if (valueTransform) {
@@ -116,8 +118,10 @@ export async function loadConfigSchema(
data,
Array.from(CONFIG_VISIBILITIES),
result.visibilityByDataPath,
+ result.deprecationByDataPath,
valueTransform,
withFilteredKeys,
+ withDeprecatedKeys,
),
}));
}
diff --git a/packages/config-loader/src/lib/schema/types.ts b/packages/config-loader/src/lib/schema/types.ts
index 53f4dec88e..75993b3c78 100644
--- a/packages/config-loader/src/lib/schema/types.ts
+++ b/packages/config-loader/src/lib/schema/types.ts
@@ -81,6 +81,13 @@ type ValidationResult = {
* The path in the key uses the form `/properties//items/additionalProperties/`
*/
visibilityBySchemaPath: Map;
+
+ /**
+ * The deprecated options that were discovered during validation.
+ *
+ * The path in the key uses the form `////`
+ */
+ deprecationByDataPath: Map;
};
/**
@@ -124,6 +131,13 @@ export type ConfigSchemaProcessingOptions = {
* Default: `false`.
*/
withFilteredKeys?: boolean;
+
+ /**
+ * Whether or not to include the `deprecatedKeys` property in the output `AppConfig`s.
+ *
+ * Default: `true`.
+ */
+ withDeprecatedKeys?: boolean;
};
/**
diff --git a/packages/config/api-report.md b/packages/config/api-report.md
index efa6b85559..df9baeb4cd 100644
--- a/packages/config/api-report.md
+++ b/packages/config/api-report.md
@@ -13,6 +13,10 @@ export type AppConfig = {
context: string;
data: JsonObject_2;
filteredKeys?: string[];
+ deprecatedKeys?: {
+ key: string;
+ description: string;
+ }[];
};
// @public
diff --git a/packages/config/src/reader.ts b/packages/config/src/reader.ts
index 129a703784..1ce54590b5 100644
--- a/packages/config/src/reader.ts
+++ b/packages/config/src/reader.ts
@@ -83,9 +83,21 @@ export class ConfigReader implements Config {
// Merge together all configs into a single config with recursive fallback
// readers, giving the first config object in the array the lowest priority.
return configs.reduce(
- (previousReader, { data, context, filteredKeys }) => {
+ (previousReader, { data, context, filteredKeys, deprecatedKeys }) => {
const reader = new ConfigReader(data, context, previousReader);
reader.filteredKeys = filteredKeys;
+
+ if (deprecatedKeys) {
+ for (const { key, description } of deprecatedKeys) {
+ // eslint-disable-next-line no-console
+ console.warn(
+ `The configuration key '${key}' of ${context} is deprecated and may be removed soon. ${
+ description || ''
+ }`,
+ );
+ }
+ }
+
return reader;
},
undefined!,
diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts
index a543233277..37c9595565 100644
--- a/packages/config/src/types.ts
+++ b/packages/config/src/types.ts
@@ -36,6 +36,12 @@ export type AppConfig = {
* This can be used to warn the user if they try to read any of these keys.
*/
filteredKeys?: string[];
+ /**
+ * A list of deprecated keys that were found in the configuration when it was loaded.
+ *
+ * This can be used to warn the user if they are using deprecated properties.
+ */
+ deprecatedKeys?: { key: string; description: string }[];
};
/**
diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md
index 070131af96..bfe1064fed 100644
--- a/packages/core-components/api-report.md
+++ b/packages/core-components/api-report.md
@@ -10,6 +10,7 @@ import { BackstageIdentityApi } from '@backstage/core-plugin-api';
import { BackstagePalette } from '@backstage/theme';
import { BackstageTheme } from '@backstage/theme';
import { BackstageUserIdentity } from '@backstage/core-plugin-api';
+import { BottomNavigationActionProps } from '@material-ui/core/BottomNavigationAction';
import { ButtonProps as ButtonProps_2 } from '@material-ui/core/Button';
import { CardHeaderProps } from '@material-ui/core/CardHeader';
import { Column } from '@material-table/core';
@@ -104,7 +105,7 @@ export type BottomLinkProps = {
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
//
// @public
-export function Breadcrumbs(props: Props_20): JSX.Element;
+export function Breadcrumbs(props: Props_19): JSX.Element;
// @public (undocumented)
export type BreadcrumbsClickableTextClassKey = 'root';
@@ -695,6 +696,14 @@ export function MissingAnnotationEmptyState(props: Props_3): JSX.Element;
// @public (undocumented)
export type MissingAnnotationEmptyStateClassKey = 'code';
+// @public
+export const MobileSidebar: (props: MobileSidebarProps) => JSX.Element | null;
+
+// @public
+export type MobileSidebarProps = {
+ children?: React_2.ReactNode;
+};
+
// Warning: (ae-missing-release-tag) "OAuthRequestDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -844,11 +853,8 @@ export type SelectItem = {
value: string | number;
};
-// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
-// Warning: (ae-missing-release-tag) "Sidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
-export function Sidebar(props: PropsWithChildren): JSX.Element;
+// @public
+export const Sidebar: (props: SidebarProps) => JSX.Element;
// Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -856,10 +862,8 @@ export function Sidebar(props: PropsWithChildren): JSX.Element;
export const SIDEBAR_INTRO_LOCAL_STORAGE =
'@backstage/core/sidebar-intro-dismissed';
-// Warning: (ae-missing-release-tag) "SidebarClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
// @public (undocumented)
-export type SidebarClassKey = 'root' | 'drawer' | 'drawerOpen';
+export type SidebarClassKey = 'drawer' | 'drawerOpen';
// Warning: (ae-missing-release-tag) "sidebarConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -877,16 +881,17 @@ export const sidebarConfig: {
selectedIndicatorWidth: number;
userBadgePadding: number;
userBadgeDiameter: number;
+ mobileSidebarHeight: number;
};
// Warning: (ae-missing-release-tag) "SidebarContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
-// @public (undocumented)
+// @public
export const SidebarContext: Context;
// Warning: (ae-missing-release-tag) "SidebarContextType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
-// @public (undocumented)
+// @public
export type SidebarContextType = {
isOpen: boolean;
setOpen: (open: boolean) => void;
@@ -1169,6 +1174,16 @@ export type SidebarDividerClassKey = 'root';
// @public
export const SidebarExpandButton: () => JSX.Element | null;
+// @public
+export const SidebarGroup: (props: SidebarGroupProps) => JSX.Element;
+
+// @public
+export interface SidebarGroupProps extends BottomNavigationActionProps {
+ children?: React_2.ReactNode;
+ priority?: number;
+ to?: string;
+}
+
// Warning: (ae-missing-release-tag) "SidebarIntro" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -1185,19 +1200,19 @@ export type SidebarIntroClassKey =
// Warning: (ae-forgotten-export) The symbol "SidebarItemProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "SidebarItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
-// @public (undocumented)
+// @public
export const SidebarItem: React_2.ForwardRefExoticComponent<
SidebarItemProps & React_2.RefAttributes
>;
-// Warning: (ae-missing-release-tag) "SidebarItemClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
// @public (undocumented)
export type SidebarItemClassKey =
| 'root'
| 'buttonItem'
| 'closed'
| 'open'
+ | 'highlightable'
+ | 'highlighted'
| 'label'
| 'iconContainer'
| 'searchRoot'
@@ -1205,29 +1220,43 @@ export type SidebarItemClassKey =
| 'searchFieldHTMLInput'
| 'searchContainer'
| 'secondaryAction'
+ | 'closedItemIcon'
+ | 'submenuArrow'
+ | 'expandButton'
+ | 'arrows'
| 'selected';
// Warning: (ae-missing-release-tag) "SidebarPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export function SidebarPage(props: PropsWithChildren<{}>): JSX.Element;
+export function SidebarPage(props: SidebarPageProps): JSX.Element;
// Warning: (ae-missing-release-tag) "SidebarPageClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type SidebarPageClassKey = 'root';
-// Warning: (ae-missing-release-tag) "SidebarPinStateContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
+// @public
+export type SidebarPageProps = {
+ children?: React_2.ReactNode;
+};
+
+// @public
export const SidebarPinStateContext: React_2.Context;
-// Warning: (ae-missing-release-tag) "SidebarPinStateContextType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
+// @public
export type SidebarPinStateContextType = {
isPinned: boolean;
toggleSidebarPinState: () => any;
+ isMobile?: boolean;
+};
+
+// @public (undocumented)
+export type SidebarProps = {
+ openDelayMs?: number;
+ closeDelayMs?: number;
+ disableExpandOnHover?: boolean;
+ children?: React_2.ReactNode;
};
// Warning: (ae-missing-release-tag) "SidebarScrollWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -2085,7 +2114,7 @@ export type SidebarSubmenuProps = {
// Warning: (ae-missing-release-tag) "SignInPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export function SignInPage(props: Props_18): JSX.Element;
+export function SignInPage(props: Props_17): JSX.Element;
// Warning: (ae-missing-release-tag) "SignInPageClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -2256,7 +2285,7 @@ export type TabBarClassKey = 'indicator' | 'flexContainer' | 'root';
// Warning: (ae-missing-release-tag) "TabbedCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export function TabbedCard(props: PropsWithChildren): JSX.Element;
+export function TabbedCard(props: PropsWithChildren): JSX.Element;
// Warning: (ae-missing-release-tag) "TabbedCardClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
diff --git a/packages/core-components/package.json b/packages/core-components/package.json
index bdfcdc0d88..83415ace48 100644
--- a/packages/core-components/package.json
+++ b/packages/core-components/package.json
@@ -58,7 +58,7 @@
"react-router": "6.0.0-beta.0",
"react-router-dom": "6.0.0-beta.0",
"react-sparklines": "^1.7.0",
- "react-syntax-highlighter": "^15.4.3",
+ "react-syntax-highlighter": "^15.4.5",
"react-text-truncate": "^0.16.0",
"react-use": "^17.2.4",
"react-virtualized-auto-sizer": "^1.0.6",
diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx
index 57653d23b5..b013affee3 100644
--- a/packages/core-components/src/layout/Page/Page.tsx
+++ b/packages/core-components/src/layout/Page/Page.tsx
@@ -20,7 +20,7 @@ import { makeStyles, ThemeProvider } from '@material-ui/core/styles';
export type PageClassKey = 'root';
-const useStyles = makeStyles(
+const useStyles = makeStyles(
() => ({
root: {
display: 'grid',
@@ -28,7 +28,7 @@ const useStyles = makeStyles(
"'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'",
gridTemplateRows: 'max-content auto 1fr',
gridTemplateColumns: 'auto 1fr auto',
- height: '100vh',
+ height: '100%',
overflowY: 'auto',
},
}),
diff --git a/packages/core-components/src/layout/Sidebar/Bar.test.tsx b/packages/core-components/src/layout/Sidebar/Bar.test.tsx
index d288aa94c5..89133997f9 100644
--- a/packages/core-components/src/layout/Sidebar/Bar.test.tsx
+++ b/packages/core-components/src/layout/Sidebar/Bar.test.tsx
@@ -14,24 +14,32 @@
* limitations under the License.
*/
-import React from 'react';
import { renderInTestApp } from '@backstage/test-utils';
+import AcUnitIcon from '@material-ui/icons/AcUnit';
+import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
+import BuildRoundedIcon from '@material-ui/icons/BuildRounded';
+import MenuBookIcon from '@material-ui/icons/MenuBook';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import BuildRoundedIcon from '@material-ui/icons/BuildRounded';
-import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
-import MenuBookIcon from '@material-ui/icons/MenuBook';
-import AcUnitIcon from '@material-ui/icons/AcUnit';
-import { Sidebar, SidebarExpandButton } from './Bar';
-import { SidebarItem, SidebarSearchField } from './Items';
-import { SidebarSubmenuItem } from './SidebarSubmenuItem';
-import { SidebarSubmenu } from './SidebarSubmenu';
-import { SidebarPinStateContext } from '.';
+import React from 'react';
+import {
+ Sidebar,
+ SidebarExpandButton,
+ SidebarItem,
+ SidebarSearchField,
+ SidebarPinStateContext,
+ SidebarSubmenu,
+ SidebarSubmenuItem,
+} from '.';
async function renderScalableSidebar() {
await renderInTestApp(
{} }}
+ value={{
+ isPinned: false,
+ isMobile: false,
+ toggleSidebarPinState: () => {},
+ }}
>
{}} to="/search" />
diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx
index bada68a028..ff907cdda3 100644
--- a/packages/core-components/src/layout/Sidebar/Bar.tsx
+++ b/packages/core-components/src/layout/Sidebar/Bar.tsx
@@ -17,23 +17,17 @@
import { makeStyles } from '@material-ui/core/styles';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import classnames from 'classnames';
-import React, { useState, useContext, PropsWithChildren, useRef } from 'react';
+import React, { useState, useContext, useRef } from 'react';
import { sidebarConfig, SidebarContext } from './config';
import { BackstageTheme } from '@backstage/theme';
import { SidebarPinStateContext } from './Page';
-import DoubleArrowRight from './icons/DoubleArrowRight';
-import DoubleArrowLeft from './icons/DoubleArrowLeft';
+import { MobileSidebar } from './MobileSidebar';
-export type SidebarClassKey = 'root' | 'drawer' | 'drawerOpen';
+/** @public */
+export type SidebarClassKey = 'drawer' | 'drawerOpen';
const useStyles = makeStyles(
theme => ({
- root: {
- zIndex: 1000,
- position: 'relative',
- overflow: 'visible',
- width: theme.spacing(7) + 1,
- },
drawer: {
display: 'flex',
flexFlow: 'column nowrap',
@@ -42,7 +36,7 @@ const useStyles = makeStyles(
left: 0,
top: 0,
bottom: 0,
- padding: 0,
+ zIndex: theme.zIndex.appBar,
background: theme.palette.navigation.background,
overflowX: 'hidden',
msOverflowStyle: 'none',
@@ -59,19 +53,6 @@ const useStyles = makeStyles(
display: 'none',
},
},
- expandButton: {
- background: 'none',
- border: 'none',
- color: theme.palette.navigation.color,
- width: '100%',
- cursor: 'pointer',
- position: 'relative',
- height: 48,
- },
- arrows: {
- position: 'absolute',
- right: 10,
- },
drawerOpen: {
width: sidebarConfig.drawerWidthOpen,
transition: theme.transitions.create('width', {
@@ -89,29 +70,44 @@ enum State {
Open,
}
-type Props = {
+/** @public */
+export type SidebarProps = {
openDelayMs?: number;
closeDelayMs?: number;
disableExpandOnHover?: boolean;
+ children?: React.ReactNode;
};
-export function Sidebar(props: PropsWithChildren) {
+/**
+ * Places the Sidebar & wraps the children providing context weather the `Sidebar` is open or not.
+ *
+ * Handles & delays hover events for expanding the `Sidebar`
+ *
+ * @param props `disableExpandOnHover` disables the default hover behaviour;
+ * `openDelayMs` & `closeDelayMs` set delay until sidebar will open/close on hover
+ * @returns
+ * @internal
+ */
+const DesktopSidebar = (props: SidebarProps) => {
const {
- disableExpandOnHover = false,
openDelayMs = sidebarConfig.defaultOpenDelayMs,
closeDelayMs = sidebarConfig.defaultCloseDelayMs,
+ disableExpandOnHover,
children,
} = props;
const classes = useStyles();
- const isSmallScreen = useMediaQuery(theme =>
- theme.breakpoints.down('md'),
+ const isSmallScreen = useMediaQuery(
+ theme => theme.breakpoints.down('md'),
+ { noSsr: true },
);
const [state, setState] = useState(State.Closed);
const hoverTimerRef = useRef();
- const { isPinned } = useContext(SidebarPinStateContext);
+ const { isPinned, toggleSidebarPinState } = useContext(
+ SidebarPinStateContext,
+ );
const handleOpen = () => {
- if (isPinned) {
+ if (isPinned || disableExpandOnHover) {
return;
}
if (hoverTimerRef.current) {
@@ -129,7 +125,7 @@ export function Sidebar(props: PropsWithChildren) {
};
const handleClose = () => {
- if (isPinned) {
+ if (isPinned || disableExpandOnHover) {
return;
}
if (hoverTimerRef.current) {
@@ -148,73 +144,60 @@ export function Sidebar(props: PropsWithChildren) {
const isOpen = (state === State.Open && !isSmallScreen) || isPinned;
+ /**
+ * Close/Open Sidebar directily without delays. Also toggles `SidebarPinState` to avoid hidden content behind Sidebar.
+ */
const setOpen = (open: boolean) => {
if (open) {
- handleOpen();
+ setState(State.Open);
+ toggleSidebarPinState();
} else {
- handleClose();
+ setState(State.Closed);
+ toggleSidebarPinState();
}
};
return (
-