diff --git a/.changeset/backstage-changelog.js b/.changeset/backstage-changelog.js
index ea8ca2300b..22038b348b 100644
--- a/.changeset/backstage-changelog.js
+++ b/.changeset/backstage-changelog.js
@@ -23,13 +23,13 @@ const {
async function getDependencyReleaseLine(changesets, dependenciesUpdated) {
if (dependenciesUpdated.length === 0) return '';
- const updatedDepenenciesList = dependenciesUpdated.map(
+ const updatedDependenciesList = dependenciesUpdated.map(
dependency => ` - ${dependency.name}@${dependency.newVersion}`,
);
// Return one `Updated dependencies` bullet instead of repeating for each changeset; this
// sacrifices the commit shas for brevity.
- return ['- Updated dependencies', ...updatedDepenenciesList].join('\n');
+ return ['- Updated dependencies', ...updatedDependenciesList].join('\n');
}
module.exports = {
diff --git a/.changeset/big-cougars-glow.md b/.changeset/big-cougars-glow.md
deleted file mode 100644
index d1630905db..0000000000
--- a/.changeset/big-cougars-glow.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/dev-utils': patch
----
-
-Add theme switcher to sidebar of dev app.
diff --git a/.changeset/big-months-float.md b/.changeset/big-months-float.md
deleted file mode 100644
index 9acb7002e7..0000000000
--- a/.changeset/big-months-float.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/plugin-catalog': patch
----
-
-Remove the "View Api" icon in the AboutCard, as the information is misleading for some users and is
-duplicated in the tabs above.
diff --git a/.changeset/blue-bikes-explode.md b/.changeset/blue-bikes-explode.md
deleted file mode 100644
index 1c10a08a46..0000000000
--- a/.changeset/blue-bikes-explode.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-'@backstage/config-loader': minor
----
-
-Update `loadConfig` to return `LoadConfigResult` instead of an array of `AppConfig`.
-
-This function is primarily used internally by other config loaders like `loadBackendConfig` which means no changes are required for most users.
-
-If you use `loadConfig` directly you will need to update your usage from:
-
-```diff
-- const appConfigs = await loadConfig(options)
-+ const { appConfigs } = await loadConfig(options)
-```
diff --git a/.changeset/cost-insights-two-crabs-evolve.md b/.changeset/cost-insights-two-crabs-evolve.md
deleted file mode 100644
index 8a932ea59c..0000000000
--- a/.changeset/cost-insights-two-crabs-evolve.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-cost-insights': patch
----
-
-Fixed generation of sample data in the example Cost Insights client
diff --git a/.changeset/curly-rings-report.md b/.changeset/curly-rings-report.md
new file mode 100644
index 0000000000..554cd0d1a8
--- /dev/null
+++ b/.changeset/curly-rings-report.md
@@ -0,0 +1,7 @@
+---
+'@backstage/core-components': patch
+'@backstage/create-app': patch
+'@backstage/plugin-catalog': patch
+---
+
+Addressed some peer dependency warnings
diff --git a/.changeset/early-cobras-explode.md b/.changeset/early-cobras-explode.md
deleted file mode 100644
index 34b4cb0e98..0000000000
--- a/.changeset/early-cobras-explode.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/catalog-client': patch
----
-
-Update to the right version of @backstage/errors
diff --git a/.changeset/eight-months-agree.md b/.changeset/eight-months-agree.md
deleted file mode 100644
index 0f117d8a40..0000000000
--- a/.changeset/eight-months-agree.md
+++ /dev/null
@@ -1,19 +0,0 @@
----
-'@backstage/plugin-gcp-projects': patch
----
-
-UI updates to GCP-projects plugin
-
-Adds the following to the project list page:
-
-- pagination
-- filtering
-- sorting
-- rows per page
-- show/hide columns
-
-Makes breadcrumb a link back to project list for the project details and new project views.
-
-In project list page, updates New project button to use RouterLink instead of `href` to avoid login prompt.
-
-In project details view, links to project details and logs now work, clicking on these will open the project or logs in GCP in new tab.
diff --git a/.changeset/empty-dots-attend.md b/.changeset/empty-dots-attend.md
deleted file mode 100644
index 3903dfa22b..0000000000
--- a/.changeset/empty-dots-attend.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-'@backstage/catalog-model': patch
-'@backstage/cli': patch
-'@backstage/config-loader': patch
-'@backstage/plugin-scaffolder': patch
----
-
-Update the json-schema dependency version.
diff --git a/.changeset/five-lies-care.md b/.changeset/five-lies-care.md
new file mode 100644
index 0000000000..dab3f55d52
--- /dev/null
+++ b/.changeset/five-lies-care.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder': patch
+---
+
+Can specify allowedOwners to the RepoUrlPicker picker in a template definition
diff --git a/.changeset/ninety-spies-prove.md b/.changeset/fluffy-countries-knock.md
similarity index 50%
rename from .changeset/ninety-spies-prove.md
rename to .changeset/fluffy-countries-knock.md
index 509aedd66f..44dddbbca7 100644
--- a/.changeset/ninety-spies-prove.md
+++ b/.changeset/fluffy-countries-knock.md
@@ -2,4 +2,4 @@
'@backstage/plugin-scaffolder-backend': patch
---
-Skip empty file names when scaffolding with nunjucks
+Bump esbuild to ^0.14.1
diff --git a/.changeset/forty-ligers-protect.md b/.changeset/forty-ligers-protect.md
deleted file mode 100644
index e3e46fb7f3..0000000000
--- a/.changeset/forty-ligers-protect.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-remove double config dep
diff --git a/.changeset/fresh-radios-compete.md b/.changeset/fresh-radios-compete.md
new file mode 100644
index 0000000000..221072eb95
--- /dev/null
+++ b/.changeset/fresh-radios-compete.md
@@ -0,0 +1,14 @@
+---
+'@backstage/plugin-kubernetes': minor
+'@backstage/plugin-kubernetes-backend': minor
+'@backstage/plugin-kubernetes-common': minor
+---
+
+Add pod metrics lookup and display in pod table.
+
+## Backwards incompatible changes
+
+If your Kubernetes distribution does not have the [metrics server](https://github.com/kubernetes-sigs/metrics-server) installed,
+you will need to set the `skipMetricsLookup` config flag to `false`.
+
+See the [configuration docs](https://backstage.io/docs/features/kubernetes/configuration) for more details.
diff --git a/.changeset/fresh-zebras-hug.md b/.changeset/fresh-zebras-hug.md
deleted file mode 100644
index c295dc2287..0000000000
--- a/.changeset/fresh-zebras-hug.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-kafka-backend': patch
----
-
-Update Kafka configuration types
diff --git a/.changeset/giant-drinks-wave.md b/.changeset/giant-drinks-wave.md
deleted file mode 100644
index ecf4c8fbdd..0000000000
--- a/.changeset/giant-drinks-wave.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/dev-utils': patch
----
-
-Migrated to using `@backstage/app-defaults`.
diff --git a/.changeset/happy-rice-tickle.md b/.changeset/happy-rice-tickle.md
deleted file mode 100644
index 1e39dc2e9c..0000000000
--- a/.changeset/happy-rice-tickle.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/backend-common': patch
----
-
-Added the `isDatabaseConflictError` function.
diff --git a/.changeset/hot-dragons-kneel.md b/.changeset/hot-dragons-kneel.md
new file mode 100644
index 0000000000..a2b6241b69
--- /dev/null
+++ b/.changeset/hot-dragons-kneel.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog-backend': patch
+---
+
+Reject catalog entities that have duplicate fields that vary only in casing.
diff --git a/.changeset/hot-walls-fail.md b/.changeset/hot-walls-fail.md
deleted file mode 100644
index 3728e10ff6..0000000000
--- a/.changeset/hot-walls-fail.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-'@backstage/create-app': patch
----
-
-Migrated the app template use the new `@backstage/app-defaults` for the `createApp` import, since the `createApp` exported by `@backstage/app-core-api` will be removed in the future.
-
-To migrate an existing application, add the latest version of `@backstage/app-defaults` as a dependency in `packages/app/package.json`, and make the following change to `packages/app/src/App.tsx`:
-
-```diff
--import { createApp, FlatRoutes } from '@backstage/core-app-api';
-+import { createApp } from '@backstage/app-defaults';
-+import { FlatRoutes } from '@backstage/core-app-api';
-```
diff --git a/.changeset/hungry-wombats-happen.md b/.changeset/hungry-wombats-happen.md
deleted file mode 100644
index 018e7ae9e8..0000000000
--- a/.changeset/hungry-wombats-happen.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/backend-common': patch
-'@backstage/cli': patch
----
-
-Update internal usage of `configLoader.loadConfig` that now returns an object instead of an array of configs.
diff --git a/.changeset/khaki-rice-kick.md b/.changeset/khaki-rice-kick.md
deleted file mode 100644
index 54819b855a..0000000000
--- a/.changeset/khaki-rice-kick.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/core-components': patch
-'@backstage/plugin-user-settings': patch
----
-
-Add Props Icon for Sidebar Item SidebarSearchField and Settings
diff --git a/.changeset/little-news-retire.md b/.changeset/little-news-retire.md
deleted file mode 100644
index 4304cf2d19..0000000000
--- a/.changeset/little-news-retire.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/plugin-bazaar': patch
-'@backstage/plugin-bazaar-backend': patch
----
-
-A Bazaar project has been extended with the following fields: size, start date (optional), end date (optional) and a responsible person.
diff --git a/.changeset/little-numbers-thank.md b/.changeset/little-numbers-thank.md
deleted file mode 100644
index f444a904c0..0000000000
--- a/.changeset/little-numbers-thank.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/core-components': patch
----
-
-Changing the `Header` styles to use more theme variables. With this the title `font-size` will not change on resizing the window.
diff --git a/.changeset/many-sloths-cross.md b/.changeset/many-sloths-cross.md
deleted file mode 100644
index 07cb6d2f61..0000000000
--- a/.changeset/many-sloths-cross.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/create-app': patch
----
-
-Refactor and add regression tests for create-app tasks
diff --git a/.changeset/moody-snails-admire.md b/.changeset/moody-snails-admire.md
deleted file mode 100644
index 7622546989..0000000000
--- a/.changeset/moody-snails-admire.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': minor
----
-
-Tweaked style insertion logic to make sure that JSS stylesheets always receive the highest priority.
diff --git a/.changeset/nasty-impalas-travel.md b/.changeset/nasty-impalas-travel.md
deleted file mode 100644
index f6eb321628..0000000000
--- a/.changeset/nasty-impalas-travel.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/backend-common': patch
----
-
-Paths can be specified in backend.reading.allow to further restrict allowed targets
diff --git a/.changeset/nine-bananas-mate.md b/.changeset/nine-bananas-mate.md
deleted file mode 100644
index e119eb722f..0000000000
--- a/.changeset/nine-bananas-mate.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/core-app-api': patch
-'@backstage/test-utils': patch
----
-
-Migrated to using new `ErrorApiError` and `ErrorApiErrorContext` names.
diff --git a/.changeset/ninety-grapes-love.md b/.changeset/ninety-grapes-love.md
deleted file mode 100644
index 9143bddd30..0000000000
--- a/.changeset/ninety-grapes-love.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/core-components': patch
-'@backstage/plugin-auth-backend': patch
----
-
-Update OAuthAdapter to create identity.token from identity.idToken if it does not exist, and prevent overwrites to identity.toke. Update login page commonProvider to prefer .token over .idToken
diff --git a/.changeset/orange-experts-approve.md b/.changeset/orange-experts-approve.md
deleted file mode 100644
index 13d1b022da..0000000000
--- a/.changeset/orange-experts-approve.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend': patch
----
-
-Adjust entity query construction to ensure sub-queries are always isolated from one another.
diff --git a/.changeset/pretty-eagles-relate.md b/.changeset/pretty-eagles-relate.md
new file mode 100644
index 0000000000..9e17be05d3
--- /dev/null
+++ b/.changeset/pretty-eagles-relate.md
@@ -0,0 +1,10 @@
+---
+'@backstage/cli': patch
+---
+
+Add cli option to minify the generated code of a plugin or backend package
+
+```
+backstage-cli plugin:build --minify
+backstage-cli backend:build --minify
+```
diff --git a/.changeset/rare-lemons-boil.md b/.changeset/rare-lemons-boil.md
deleted file mode 100644
index b882ab0b91..0000000000
--- a/.changeset/rare-lemons-boil.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/core-app-api': patch
----
-
-Deprecated the `BackstagePluginWithAnyOutput` type.
diff --git a/.changeset/rich-pillows-cough.md b/.changeset/rich-pillows-cough.md
deleted file mode 100644
index 959eb69214..0000000000
--- a/.changeset/rich-pillows-cough.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-'@backstage/backend-common': patch
----
-
-Make sure that server builder `start()` propagates errors (such as failing to bind to the required port) properly and doesn't resolve the promise prematurely.
-
-After this change, the backend logger will be able to actually capture the error as it happens:
-
-```
-2021-11-11T10:54:21.334Z backstage info Initializing http server
-2021-11-11T10:54:21.335Z backstage error listen EADDRINUSE: address already in use :::7000 code=EADDRINUSE errno=-48 syscall=listen address=:: port=7000
-```
diff --git a/.changeset/rotten-clouds-kick.md b/.changeset/rotten-clouds-kick.md
deleted file mode 100644
index 2c4772c6e5..0000000000
--- a/.changeset/rotten-clouds-kick.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/core-app-api': patch
----
-
-Start exporting and marking several types as public to address errors in the API report.
diff --git a/.changeset/search-hip-schools-burn.md b/.changeset/search-hip-schools-burn.md
new file mode 100644
index 0000000000..12cef5f5ca
--- /dev/null
+++ b/.changeset/search-hip-schools-burn.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-search': patch
+---
+
+Export SearchApi interface from plugin
diff --git a/.changeset/sharp-meals-fetch.md b/.changeset/sharp-meals-fetch.md
deleted file mode 100644
index 1d3df65ba4..0000000000
--- a/.changeset/sharp-meals-fetch.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/core-plugin-api': minor
----
-
-Removed the unused `UserFlags` type.
diff --git a/.changeset/sharp-moons-jog.md b/.changeset/sharp-moons-jog.md
deleted file mode 100644
index d3329af08e..0000000000
--- a/.changeset/sharp-moons-jog.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/core-plugin-api': patch
----
-
-Deprecated the `theme` property on `AppTheme`, replacing it with `Provider`. See https://backstage.io/docs/api/deprecations#app-theme for more details.
diff --git a/.changeset/shiny-starfishes-float.md b/.changeset/shiny-starfishes-float.md
deleted file mode 100644
index 0673791375..0000000000
--- a/.changeset/shiny-starfishes-float.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-Added a scaffolder backend module template for the `create` command.
diff --git a/.changeset/silent-taxis-tan.md b/.changeset/silent-taxis-tan.md
deleted file mode 100644
index 286ef40a4c..0000000000
--- a/.changeset/silent-taxis-tan.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/core-plugin-api': minor
----
-
-Remove exports of unused types(`RouteOptions` and `RoutePath`).
diff --git a/.changeset/sixty-files-talk.md b/.changeset/sixty-files-talk.md
new file mode 100644
index 0000000000..67d8c48b7e
--- /dev/null
+++ b/.changeset/sixty-files-talk.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-analytics-module-ga': patch
+---
+
+Support self hosted analytics.js script via `scriptSrc` config option
diff --git a/.changeset/slow-moles-act.md b/.changeset/slow-moles-act.md
deleted file mode 100644
index ab920bfa5d..0000000000
--- a/.changeset/slow-moles-act.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/test-utils': patch
----
-
-Migrated to using `createSpecializedApp`.
diff --git a/.changeset/sour-cameras-hide.md b/.changeset/sour-cameras-hide.md
deleted file mode 100644
index a31e7a13c3..0000000000
--- a/.changeset/sour-cameras-hide.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-Fixed a bug where calling `backstage-cli backend:bundle --build-dependencies` with no dependencies to be built would cause all monorepo packages to be built instead.
diff --git a/.changeset/spicy-rice-build.md b/.changeset/spicy-rice-build.md
deleted file mode 100644
index c0322d778a..0000000000
--- a/.changeset/spicy-rice-build.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/backend-common': patch
----
-
-Do not redact empty or one-character strings. These imply that it's just a test or local dev, and unnecessarily ruin the log output.
diff --git a/.changeset/proud-bottles-cheat.md b/.changeset/swift-clocks-cry.md
similarity index 51%
rename from .changeset/proud-bottles-cheat.md
rename to .changeset/swift-clocks-cry.md
index 0c724352c7..8f37a2e340 100644
--- a/.changeset/proud-bottles-cheat.md
+++ b/.changeset/swift-clocks-cry.md
@@ -2,4 +2,4 @@
'@backstage/backend-common': patch
---
-Add `AbortSignal` support to `UrlReader`
+Add knexConfig config section
diff --git a/.changeset/tame-buckets-move.md b/.changeset/tame-buckets-move.md
deleted file mode 100644
index f7ee3deb90..0000000000
--- a/.changeset/tame-buckets-move.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/plugin-azure-devops': patch
-'@backstage/plugin-azure-devops-backend': patch
-'@backstage/plugin-azure-devops-common': minor
----
-
-Improved Date handling for the Azure DevOps set of plugins by using strings and letting the frontend handle the conversion to DateTime
diff --git a/.changeset/techdocs-chatt-months-report-too.md b/.changeset/techdocs-chatt-months-report-too.md
deleted file mode 100644
index 612a4506a5..0000000000
--- a/.changeset/techdocs-chatt-months-report-too.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-techdocs': patch
----
-
-Fixed entity triplet case handling for certain locales.
diff --git a/.changeset/techdocs-chatty-months-report.md b/.changeset/techdocs-chatty-months-report.md
deleted file mode 100644
index 7d3fa773bf..0000000000
--- a/.changeset/techdocs-chatty-months-report.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/techdocs-common': patch
----
-
-Added OpenStack Swift case migration support.
diff --git a/.changeset/techdocs-famous-donuts-warn.md b/.changeset/techdocs-famous-donuts-warn.md
deleted file mode 100644
index 2fd9346ee2..0000000000
--- a/.changeset/techdocs-famous-donuts-warn.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@techdocs/cli': patch
----
-
-Add support for specifying bucketRootPath for AWS and GCS publishers
diff --git a/.changeset/techdocs-orange-cougars-relax.md b/.changeset/techdocs-orange-cougars-relax.md
deleted file mode 100644
index 376de6a0e5..0000000000
--- a/.changeset/techdocs-orange-cougars-relax.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/techdocs-common': patch
-'@backstage/plugin-techdocs-backend': patch
----
-
-Allow amazon web services s3 buckets to pass an server side encryption configuration so they can publish to encrypted buckets
diff --git a/.changeset/techdocs-sharp-knives-unite.md b/.changeset/techdocs-sharp-knives-unite.md
deleted file mode 100644
index aa3625b883..0000000000
--- a/.changeset/techdocs-sharp-knives-unite.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-techdocs': patch
----
-
-Use a better checkbox rendering in a task list.
diff --git a/.changeset/techdocs-silver-plums-speak.md b/.changeset/techdocs-silver-plums-speak.md
deleted file mode 100644
index 84d04fc1ef..0000000000
--- a/.changeset/techdocs-silver-plums-speak.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-techdocs': patch
----
-
-Updates reader component used to display techdocs documentation. A previous change made this component not usable out of a page which don't have entityRef in url parameters. Reader component EntityRef parameter is now used instead of url parameters. Techdocs documentation component can now be used in our custom pages.
diff --git a/.changeset/tender-gorillas-peel.md b/.changeset/tender-gorillas-peel.md
deleted file mode 100644
index f7d7022740..0000000000
--- a/.changeset/tender-gorillas-peel.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-bump `@rollup/plugin-commonjs` from 17.1.0 to 21.0.1
diff --git a/.changeset/three-news-worry.md b/.changeset/three-news-worry.md
new file mode 100644
index 0000000000..24c7daa240
--- /dev/null
+++ b/.changeset/three-news-worry.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-bazaar-backend': patch
+---
+
+Handle migration error when old data is present in the database
diff --git a/.changeset/tidy-beans-reflect.md b/.changeset/tidy-beans-reflect.md
deleted file mode 100644
index 20796075f0..0000000000
--- a/.changeset/tidy-beans-reflect.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-Introduces new `backstage-cli create` command to replace `create-plugin` and make space for creating a wider array of things. The create command also adds a new template for creating isomorphic common plugin packages.
diff --git a/.changeset/twenty-swans-matter.md b/.changeset/twenty-swans-matter.md
deleted file mode 100644
index 55cd3d6dc3..0000000000
--- a/.changeset/twenty-swans-matter.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-'@backstage/core-app-api': patch
----
-
-The `createApp` function from `@backstage/core-app-api` has been deprecated, with two new options being provided as a replacement.
-
-The first and most commonly used one is `createApp` from the new `@backstage/app-defaults` package, which behaves just like the existing `createApp`. In the future this method is likely to be expanded to add more APIs and other pieces into the default setup, for example the Utility APIs from `@backstage/integration-react`.
-
-The other option that we now provide is to use `createSpecializedApp` from `@backstage/core-app-api`. This is a more low-level API where you need to provide a full set of options, including your own `components`, `icons`, `defaultApis`, and `themes`. The `createSpecializedApp` way of creating an app is particularly useful if you are not using `@backstage/core-components` or MUI, as it allows you to avoid those dependencies completely.
diff --git a/.changeset/violet-panthers-care.md b/.changeset/violet-panthers-care.md
deleted file mode 100644
index 7a43c87f37..0000000000
--- a/.changeset/violet-panthers-care.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend': patch
----
-
-adds getDefaultProcessor method to CatalogBuilder
diff --git a/.changeset/yellow-deers-act.md b/.changeset/yellow-deers-act.md
deleted file mode 100644
index a9008ea41e..0000000000
--- a/.changeset/yellow-deers-act.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/core-plugin-api': patch
----
-
-Deprecated the `Error` and `ErrorContext` types, replacing them with identical `ErrorApiError` and `ErrorApiErrorContext` types.
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 9be92d294e..ed4b61099f 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -8,6 +8,8 @@
/docs/features/techdocs @backstage/techdocs-core
/docs/features/search @backstage/techdocs-core
/docs/assets/search @backstage/techdocs-core
+/plugins/allure @backstage/reviewers @deepak-bhardwaj-ps
+/plugins/circleci @backstage/reviewers @adamdmharvey
/plugins/code-coverage @backstage/reviewers @alde @nissayeva
/plugins/code-coverage-backend @backstage/reviewers @alde @nissayeva
/plugins/cost-insights @backstage/silver-lining
@@ -18,6 +20,20 @@
/plugins/techdocs-backend @backstage/techdocs-core
/plugins/ilert @backstage/reviewers @yacut
/plugins/home @backstage/techdocs-core
+/plugins/azure-devops @backstage/reviewers @marleypowell @awanlin
+/plugins/azure-devops-backend @backstage/reviewers @marleypowell @awanlin
+/plugins/azure-devops-common @backstage/reviewers @marleypowell @awanlin
+/plugins/jenkins @backstage/reviewers @timja
+/plugins/jenkins-backend @backstage/reviewers @timja
+/plugins/kafka @backstage/reviewers @nirga
+/plugins/kafka-backend @backstage/reviewers @nirga
+/plugins/scaffolder-backend-module-yeoman @backstage/reviewers @pawelmitka
+/plugins/scaffolder-backend-module-rails @backstage/reviewers @angeliski
+/plugins/git-release-manager @backstage/reviewers @erikengervall
+/tech-insights-backend @backstage/reviewers @xantier @iain-b
+/tech-insights-backend-module-jsonfc @backstage/reviewers @xantier @iain-b
+/tech-insights-tech-insights-common @backstage/reviewers @xantier @iain-b
+/tech-insights-tech-insights-node @backstage/reviewers @xantier @iain-b
/packages/embedded-techdocs-app @backstage/techdocs-core
/packages/search-common @backstage/techdocs-core
/packages/techdocs-cli @backstage/techdocs-core
diff --git a/.github/stale.yml b/.github/stale.yml
deleted file mode 100644
index 3cd0d53720..0000000000
--- a/.github/stale.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-# Number of days of inactivity before an issue becomes stale
-daysUntilStale: 60
-# Number of days of inactivity before a stale issue is closed
-daysUntilClose: 7
-# Issues with these labels will never be considered stale
-exemptLabels:
- - pinned
- - security
- - plugin
- - help wanted
- - good first issue
- - rfc
-# Label to use when marking an issue as stale
-staleLabel: stale
-# Comment to post when marking an issue as stale. Set to `false` to disable
-markComment: >
- This issue has been automatically marked as stale because it has not had
- recent activity. It will be closed if no further activity occurs. Thank you
- for your contributions.
-# Comment to post when closing a stale issue. Set to `false` to disable
-closeComment: false
diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt
index d65e31b4c4..4ec62923d8 100644
--- a/.github/styles/vocab.txt
+++ b/.github/styles/vocab.txt
@@ -189,6 +189,7 @@ oidc
Okta
onboarding
Onboarding
+OpenShift
orgs
pagerduty
pageview
diff --git a/.github/workflows/snyk-github-issue-sync.yml b/.github/workflows/snyk-github-issue-sync.yml
index 0c50b2c505..7b93374609 100644
--- a/.github/workflows/snyk-github-issue-sync.yml
+++ b/.github/workflows/snyk-github-issue-sync.yml
@@ -6,6 +6,8 @@ on:
jobs:
sync:
+ if: github.repository == 'backstage/backstage' # prevent running on forks
+
runs-on: ubuntu-latest
strategy:
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 0000000000..96a2a20109
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,30 @@
+name: 'Stale workflow'
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: '*/10 * * * *' # run every 10 minutes as it also removes labels.
+
+jobs:
+ stale:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/stale@main
+ id: stale
+ with:
+ stale-issue-message: >
+ This issue has been automatically marked as stale because it has not had
+ recent activity. It will be closed if no further activity occurs. Thank you
+ for your contributions.
+ days-before-issue-stale: 60
+ days-before-issue-close: 7
+ exempt-issue-labels: 'pinned,security,plugin,help wanted,good first issue,rfc'
+ stale-issue-label: stale
+ stale-pr-message: >
+ This PR has been automatically marked as stale because it has not had
+ recent activity from the author. It will be closed if no further activity occurs.
+ If you are the author and the PR has been closed, feel free to re-open the PR and continue the contribution!
+ days-before-pr-stale: 7
+ days-before-pr-close: 3
+ exempt-pr-labels: reviewer-approved,awaiting-review
+ stale-pr-label: stale
+ operations-per-run: 100
diff --git a/.tugboat/config.yml b/.tugboat/config.yml
index b8c3c127c8..883708bef7 100644
--- a/.tugboat/config.yml
+++ b/.tugboat/config.yml
@@ -1,7 +1,7 @@
services:
backstage:
image: tugboatqa/node:lts
- expose: 7000
+ expose: 7007
default: true
commands:
init:
@@ -14,4 +14,4 @@ services:
- 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:7000
+ - wget -O /dev/null -o /dev/null --tries=30 --timeout=5 --retry-connrefused http://localhost:7007
diff --git a/ADOPTERS.md b/ADOPTERS.md
index 92912fe144..529c22d144 100644
--- a/ADOPTERS.md
+++ b/ADOPTERS.md
@@ -69,3 +69,7 @@
| [Unity](https://unity.com) | [Ted Cordery](https://github.com/TeddyBallGame) | A centralized service catalog with documentation for our service engineers. |
| [PicPay](https://www.picpay.com) | [Luis Baroni](https://github.com/lcsbaroni), [Renata Poluceno](https://github.com/renatapoluceno), [PicPay](https://github.com/picpay) | Developer portal for building services throught templates, service catalog with ownership of services, documentation and metrics providing autonomy and visibility for all. |
| [Epic Games](https://www.epicgames.com) | [Brian Jung](https://github.com/brian-at-epic), [Jeff Goldian](https://github.com/jeffgoldian-Epic) | Developer Portal: Service Catalog, Documentation, Software Templates and more making our internal teams' lives easier! |
+| [Globo](https://globo.com) | [Carlos Gusmão](https://github.com/caeugusmao), [Guilherme Vierno](https://github.com/vierno), [Denis Aoki](https://github.com/dnsaoki2), [Maycon Dionisio](https://github.com/MayconDionisio), | Reduce the friction of accessing the information engineers need about Globo's digital services through a coherent and centralized experience. |
+| [QBE](https://www.qbe.com/) | [Daniel Steel](https://github.com/danielsteelqbe), [Pete Jespers](https://github.com/petejespersqbe) | Developer portal allowing our global teams to explore and create applications, documentation and cloud infrastructure easily and quickly 🚀 |
+| [LogMeIn](https://www.logmein.com) | [Lorenzo Orsatti](https://github.com/lorsatti) | Improve onboarding experience of new developers. Discover faster and painlessly developer documentation, API definitions and team information. Provide useful dev metrics in a central place. Provide easy-to-use templates for new services. |
+| [Telstra](https://www.telstra.com.au) | [@kiranpatel11](https://github.com/kiranpatel11), [JasonC](https://github.com/JasonC17) | Primary usage: software catalog and templates Emerging usage : TechDocs, Explore Ecosystem, TechRadar, etc |
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 99d63cf0b4..31f912f43e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -122,7 +122,9 @@ We use [changesets](https://github.com/atlassian/changesets) to help us prepare
Any time a patch, minor, or major change aligning to [Semantic Versioning](https://semver.org) is made to any published package in `packages/` or `plugins/`, a changeset should be used. It helps to align your change to the [Backstage stability index](https://backstage.io/docs/overview/stability-index) for the package you are changing, for example, when to provide additional clarity on deprecation or impacting changes which will then be included into CHANGELOGs.
-In general, changesets are not needed for the documentation, build utilities, contributed samples in `contrib/`, or the [example `packages/app`](packages/app).
+In general, changesets are only needed for changes to packages within `packages/` or `plugins/` directories, and only for the packages that are not marked as `private`. Changesets are also not needed for changes that do not affect the published version of each package, for example changes to tests or in-line source code comments.
+
+Changesets **are** needed for new packages, as that is what triggers the package to be part of the next release.
### How to create a changeset
diff --git a/app-config.yaml b/app-config.yaml
index 8aa3bc569c..4adaea0da4 100644
--- a/app-config.yaml
+++ b/app-config.yaml
@@ -23,9 +23,14 @@ app:
title: '#backstage'
backend:
- baseUrl: http://localhost:7000
+ # Used for enabling authentication, secret is shared by all backend plugins
+ # See backend-to-backend-auth.md in the docs for information on the format
+ # auth:
+ # keys:
+ # - secret: ${BACKEND_SECRET}
+ baseUrl: http://localhost:7007
listen:
- port: 7000
+ port: 7007
database:
client: sqlite3
connection: ':memory:'
@@ -272,24 +277,6 @@ scaffolder:
# email: scaffolder@backstage.io
# Use to customize the default commit message when new components are created
# defaultCommitMessage: 'Initial commit'
- github:
- token: ${GITHUB_TOKEN}
- visibility: public # or 'internal' or 'private'
- gitlab:
- api:
- baseUrl: https://gitlab.com
- token: ${GITLAB_TOKEN}
- visibility: public # or 'internal' or 'private'
- azure:
- baseUrl: https://dev.azure.com/{your-organization}
- api:
- token: ${AZURE_TOKEN}
- bitbucket:
- api:
- host: https://bitbucket.org
- username: ${BITBUCKET_USERNAME}
- token: ${BITBUCKET_TOKEN}
- visibility: public # or or 'private'
auth:
### Add auth.keyStore.provider to more granularly control how to store JWK data when running
diff --git a/contrib/chart/backstage/files/app-config.development.yaml.tpl b/contrib/chart/backstage/files/app-config.development.yaml.tpl
index 6d3ded16c1..105ba3b259 100644
--- a/contrib/chart/backstage/files/app-config.development.yaml.tpl
+++ b/contrib/chart/backstage/files/app-config.development.yaml.tpl
@@ -1,7 +1,7 @@
backend:
lighthouseHostname: {{ include "lighthouse.serviceName" . | quote }}
listen:
- port: {{ .Values.appConfig.backend.listen.port | default 7000 }}
+ port: {{ .Values.appConfig.backend.listen.port | default 7007 }}
database:
client: {{ .Values.appConfig.backend.database.client | quote }}
connection:
diff --git a/contrib/chart/backstage/templates/ingress.yaml b/contrib/chart/backstage/templates/ingress.yaml
index 7231afda13..9340467800 100644
--- a/contrib/chart/backstage/templates/ingress.yaml
+++ b/contrib/chart/backstage/templates/ingress.yaml
@@ -1,7 +1,13 @@
{{- $frontendUrl := urlParse .Values.appConfig.app.baseUrl}}
{{- $backendUrl := urlParse .Values.appConfig.backend.baseUrl}}
{{- $lighthouseUrl := urlParse .Values.appConfig.lighthouse.baseUrl}}
+
+{{/* Determine the api type for the ingress */}}
+{{- if lt .Capabilities.KubeVersion.Minor "19" }}
apiVersion: networking.k8s.io/v1beta1
+{{- else if ge .Capabilities.KubeVersion.Minor "19" }}
+apiVersion: networking.k8s.io/v1
+{{- end }}
kind: Ingress
metadata:
name: {{ include "backstage.fullname" . }}-ingress
diff --git a/contrib/chart/backstage/values.yaml b/contrib/chart/backstage/values.yaml
index 9f70dac6dc..6ffe076e57 100644
--- a/contrib/chart/backstage/values.yaml
+++ b/contrib/chart/backstage/values.yaml
@@ -26,7 +26,7 @@ backend:
repository: martinaif/backstage-k8s-demo-backend
tag: 20210423T1550
pullPolicy: IfNotPresent
- containerPort: 7000
+ containerPort: 7007
serviceType: ClusterIP
postgresCertMountEnabled: true
resources:
@@ -96,7 +96,7 @@ appConfig:
backend:
baseUrl: https://demo.example.com
listen:
- port: 7000
+ port: 7007
cors:
origin: https://demo.example.com
database:
diff --git a/contrib/docker/devops/makefile b/contrib/docker/devops/makefile
index 3ae89a161b..048edc8886 100644
--- a/contrib/docker/devops/makefile
+++ b/contrib/docker/devops/makefile
@@ -9,8 +9,8 @@ docker_name_prefix := backstage-make
# to the host computer
frontend_port := 3000
frontend_host_port := 3000
-backend_port := 7000
-backend_host_port := 7000
+backend_port := 7007
+backend_host_port := 7007
# path to this "makefile"
my_dir_path = $(dir $(CURDIR)/$(firstword $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))
@@ -191,10 +191,10 @@ test: check-tests
check: check-code check-docs check-type-dependencies check-styles
# run development instance
-# BUG: the frontend seems to run on "$(backend_port)" (7000 default).
+# BUG: the frontend seems to run on "$(backend_port)" (7007 default).
# The documentation states "This is going to start two things,
-# the frontend (:3000) and the backend (:7000)."
-# However, the frontend seems to end up running on 7000.
+# the frontend (:3000) and the backend (:7007)."
+# However, the frontend seems to end up running on 7007.
.PHONY: dev
dev: build
@docker run --rm -it \
diff --git a/contrib/kubernetes/basic_kubernetes_example_with_helm/backend.yaml b/contrib/kubernetes/basic_kubernetes_example_with_helm/backend.yaml
index f0695753fd..c8d60a9c1b 100644
--- a/contrib/kubernetes/basic_kubernetes_example_with_helm/backend.yaml
+++ b/contrib/kubernetes/basic_kubernetes_example_with_helm/backend.yaml
@@ -22,6 +22,6 @@ spec:
image: spotify/backstage-backend:latest
imagePullPolicy: IfNotPresent
ports:
- - containerPort: 7000
+ - containerPort: 7007
name: backend
protocol: TCP
diff --git a/contrib/kubernetes/basic_kubernetes_example_with_helm/backstage/values.yaml b/contrib/kubernetes/basic_kubernetes_example_with_helm/backstage/values.yaml
index d91808ed28..a293dc12fb 100644
--- a/contrib/kubernetes/basic_kubernetes_example_with_helm/backstage/values.yaml
+++ b/contrib/kubernetes/basic_kubernetes_example_with_helm/backstage/values.yaml
@@ -63,7 +63,7 @@ backend:
pullPolicy: IfNotPresent
service:
type: ClusterIP
- port: 7000
+ port: 7007
ingress:
enabled: false
annotations:
diff --git a/contrib/kubernetes/basic_kubernetes_example_with_helm/service.yaml b/contrib/kubernetes/basic_kubernetes_example_with_helm/service.yaml
index 4d947b7afc..e1da35b598 100644
--- a/contrib/kubernetes/basic_kubernetes_example_with_helm/service.yaml
+++ b/contrib/kubernetes/basic_kubernetes_example_with_helm/service.yaml
@@ -30,6 +30,6 @@ spec:
component: backend
ports:
- name: backend
- port: 7000
+ port: 7007
protocol: TCP
targetPort: backend
diff --git a/cypress/cypress.json b/cypress/cypress.json
index 3ef3df8e65..7335d0f62a 100644
--- a/cypress/cypress.json
+++ b/cypress/cypress.json
@@ -1,5 +1,5 @@
{
- "baseUrl": "http://localhost:7000",
+ "baseUrl": "http://localhost:7007",
"integrationFolder": "./src/integration",
"supportFile": "./src/support",
"fixturesFolder": "./src/fixtures",
diff --git a/cypress/yarn.lock b/cypress/yarn.lock
index baf0d92715..96bc765d0d 100644
--- a/cypress/yarn.lock
+++ b/cypress/yarn.lock
@@ -13,9 +13,9 @@
figures "^1.7.0"
"@cypress/request@^2.88.5":
- version "2.88.5"
- resolved "https://registry.npmjs.org/@cypress/request/-/request-2.88.5.tgz#8d7ecd17b53a849cfd5ab06d5abe7d84976375d7"
- integrity sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA==
+ version "2.88.10"
+ resolved "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz#b66d76b07f860d3a4b8d7a0604d020c662752cce"
+ integrity sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.8.0"
@@ -24,19 +24,17 @@
extend "~3.0.2"
forever-agent "~0.6.1"
form-data "~2.3.2"
- har-validator "~5.1.3"
- http-signature "~1.2.0"
+ http-signature "~1.3.6"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.19"
- oauth-sign "~0.9.0"
performance-now "^2.1.0"
qs "~6.5.2"
safe-buffer "^5.1.2"
tough-cookie "~2.5.0"
tunnel-agent "^0.6.0"
- uuid "^3.3.2"
+ uuid "^8.3.2"
"@cypress/xvfb@^1.2.4":
version "1.2.4"
@@ -68,16 +66,6 @@
resolved "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47"
integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==
-ajv@^6.12.3:
- version "6.12.6"
- resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
- integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
- dependencies:
- fast-deep-equal "^3.1.1"
- fast-json-stable-stringify "^2.0.0"
- json-schema-traverse "^0.4.1"
- uri-js "^4.2.2"
-
ansi-escapes@^3.0.0:
version "3.2.0"
resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
@@ -539,16 +527,6 @@ extsprintf@^1.2.0:
resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
-fast-deep-equal@^3.1.1:
- version "3.1.3"
- resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
- integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
-
-fast-json-stable-stringify@^2.0.0:
- version "2.1.0"
- resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
- integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
-
fd-slicer@~1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
@@ -645,19 +623,6 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0:
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.5.tgz#bc18864a6c9fc7b303f2e2abdb9155ad178fbe29"
integrity sha512-kBBSQbz2K0Nyn+31j/w36fUfxkBW9/gfwRWdUY1ULReH3iokVJgddZAFcD1D0xlgTmFxJCbUkUclAlc6/IDJkw==
-har-schema@^2.0.0:
- version "2.0.0"
- resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
- integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
-
-har-validator@~5.1.3:
- version "5.1.5"
- resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd"
- integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==
- dependencies:
- ajv "^6.12.3"
- har-schema "^2.0.0"
-
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
@@ -675,14 +640,14 @@ has-flag@^4.0.0:
resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
-http-signature@~1.2.0:
- version "1.2.0"
- resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
- integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
+http-signature@~1.3.6:
+ version "1.3.6"
+ resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9"
+ integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==
dependencies:
assert-plus "^1.0.0"
- jsprim "^1.2.2"
- sshpk "^1.7.0"
+ jsprim "^2.0.2"
+ sshpk "^1.14.1"
human-signals@^1.1.1:
version "1.1.1"
@@ -796,15 +761,10 @@ jsbn@~0.1.0:
resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
-json-schema-traverse@^0.4.1:
- version "0.4.1"
- resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
- integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
-
-json-schema@0.2.3:
- version "0.2.3"
- resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
- integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
+json-schema@0.4.0:
+ version "0.4.0"
+ resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5"
+ integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==
json-stringify-safe@~5.0.1:
version "5.0.1"
@@ -820,14 +780,14 @@ jsonfile@^6.0.1:
optionalDependencies:
graceful-fs "^4.1.6"
-jsprim@^1.2.2:
- version "1.4.1"
- resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
- integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
+jsprim@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d"
+ integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==
dependencies:
assert-plus "1.0.0"
extsprintf "1.3.0"
- json-schema "0.2.3"
+ json-schema "0.4.0"
verror "1.10.0"
lazy-ass@^1.6.0:
@@ -985,11 +945,6 @@ number-is-nan@^1.0.0:
resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
-oauth-sign@~0.9.0:
- version "0.9.0"
- resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
- integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
-
object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -1084,7 +1039,7 @@ punycode@1.3.2:
resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
-punycode@^2.1.0, punycode@^2.1.1:
+punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
@@ -1191,7 +1146,7 @@ slice-ansi@0.0.4:
resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=
-sshpk@^1.7.0:
+sshpk@^1.14.1:
version "1.16.1"
resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
@@ -1353,13 +1308,6 @@ untildify@^4.0.0:
resolved "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
-uri-js@^4.2.2:
- version "4.4.1"
- resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
- integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
- dependencies:
- punycode "^2.1.0"
-
url@^0.11.0:
version "0.11.0"
resolved "https://registry.npmjs.org/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
@@ -1373,10 +1321,10 @@ util-deprecate@~1.0.1:
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
-uuid@^3.3.2:
- version "3.4.0"
- resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
- integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
+uuid@^8.3.2:
+ version "8.3.2"
+ resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
+ integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
verror@1.10.0:
version "1.10.0"
diff --git a/docs/api/deprecations.md b/docs/api/deprecations.md
index 774c8ef03d..aca7e42d96 100644
--- a/docs/api/deprecations.md
+++ b/docs/api/deprecations.md
@@ -54,3 +54,10 @@ const darkTheme = {
),
};
```
+
+Note that the existing `AppTheme` type still requires the `theme` property to be
+set since it's the type that's consumed in the `AppThemeApi`, and it would be a
+breaking change to make `theme` optional. This means that if you currently
+construct the themes that you pass on to `createApp` using `AppTheme` as an
+intermediate type, you will need to work around this in some way, for example by
+passing the themes to `createApp` more directly.
diff --git a/docs/api/utility-apis.md b/docs/api/utility-apis.md
index 133653df44..14920513af 100644
--- a/docs/api/utility-apis.md
+++ b/docs/api/utility-apis.md
@@ -291,7 +291,7 @@ The figure below shows the relationship between
fooApiRef.
-
+
The current method for connecting Utility API providers and consumers is via the
diff --git a/docs/architecture-decisions/adr012-use-luxon-locale-and-date-presets.md b/docs/architecture-decisions/adr012-use-luxon-locale-and-date-presets.md
index a9a5ef5849..0893c105f4 100644
--- a/docs/architecture-decisions/adr012-use-luxon-locale-and-date-presets.md
+++ b/docs/architecture-decisions/adr012-use-luxon-locale-and-date-presets.md
@@ -1,6 +1,6 @@
---
id: adrs-adr012
-title: ADR000: Use Luxon.toLocaleString and date/time presets
+title: ADR012: Use Luxon.toLocaleString and date/time presets
description: Architecture Decision Record (ADR) for using Luxon's toLocaleString method and date/time presets for displaying dates and times
---
diff --git a/docs/assets/software-templates/grouped-templates.png b/docs/assets/software-templates/grouped-templates.png
new file mode 100644
index 0000000000..9a3689ad1e
Binary files /dev/null and b/docs/assets/software-templates/grouped-templates.png differ
diff --git a/docs/auth/add-auth-provider.md b/docs/auth/add-auth-provider.md
index 472a6a0abc..07d6869fe3 100644
--- a/docs/auth/add-auth-provider.md
+++ b/docs/auth/add-auth-provider.md
@@ -229,7 +229,7 @@ name.
### Test the new provider
-You can `curl -i localhost:7000/api/auth/providerA/start` and which should
+You can `curl -i localhost:7007/api/auth/providerA/start` and which should
provide a `302` redirect with a `Location` header. Paste the url from that
header into a web browser and you should be able to trigger the authorization
flow.
diff --git a/docs/auth/atlassian/provider.md b/docs/auth/atlassian/provider.md
index 38774ca3cc..cded6e73ca 100644
--- a/docs/auth/atlassian/provider.md
+++ b/docs/auth/atlassian/provider.md
@@ -28,7 +28,7 @@ Name your integration and click on the `Create` button.
Settings for local development:
-- Callback URL: `http://localhost:7000/api/auth/atlassian`
+- Callback URL: `http://localhost:7007/api/auth/atlassian`
- Use rotating refresh tokens
- For permissions, you **must** enable `View user profile` for the currently
logged-in user, under `User identity API`
diff --git a/docs/auth/auth0/provider.md b/docs/auth/auth0/provider.md
index 05553c3fd3..80106a73c9 100644
--- a/docs/auth/auth0/provider.md
+++ b/docs/auth/auth0/provider.md
@@ -17,7 +17,7 @@ provider that can authenticate users using OAuth.
- Application type: Single Page Web Application
4. Click on the Settings tab
5. Add under `Application URIs` > `Allowed Callback URLs`:
- `http://localhost:7000/api/auth/auth0/handler/frame`
+ `http://localhost:7007/api/auth/auth0/handler/frame`
6. Click `Save Changes`
## Configuration
diff --git a/docs/auth/bitbucket/provider.md b/docs/auth/bitbucket/provider.md
index ae09d5dbee..63dfb815e0 100644
--- a/docs/auth/bitbucket/provider.md
+++ b/docs/auth/bitbucket/provider.md
@@ -20,7 +20,7 @@ Click Add Consumer.
Settings for local development:
- Application name: Backstage (or your custom app name)
-- Callback URL: `http://localhost:7000/api/auth/bitbucket`
+- Callback URL: `http://localhost:7007/api/auth/bitbucket`
- Other are optional
- (IMPORTANT) **Permissions: Account - Read, Workspace membership - Read**
diff --git a/docs/auth/github/provider.md b/docs/auth/github/provider.md
index d8803e392d..14b99bfea5 100644
--- a/docs/auth/github/provider.md
+++ b/docs/auth/github/provider.md
@@ -24,7 +24,7 @@ Settings for local development:
- Application name: Backstage (or your custom app name)
- Homepage URL: `http://localhost:3000`
-- Authorization callback URL: `http://localhost:7000/api/auth/github`
+- Authorization callback URL: `http://localhost:7007/api/auth/github`
## Configuration
diff --git a/docs/auth/gitlab/provider.md b/docs/auth/gitlab/provider.md
index fd64ddac14..90939a1f03 100644
--- a/docs/auth/gitlab/provider.md
+++ b/docs/auth/gitlab/provider.md
@@ -17,7 +17,7 @@ should point to your Backstage backend auth handler.
Settings for local development:
- Name: Backstage (or your custom app name)
-- Redirect URI: `http://localhost:7000/api/auth/gitlab/handler/frame`
+- Redirect URI: `http://localhost:7007/api/auth/gitlab/handler/frame`
- Scopes: read_user
## Configuration
diff --git a/docs/auth/google/provider.md b/docs/auth/google/provider.md
index 6216b21704..d3cd8f2dd2 100644
--- a/docs/auth/google/provider.md
+++ b/docs/auth/google/provider.md
@@ -26,7 +26,7 @@ To support Google authentication, you must create OAuth credentials:
- `Name`: Backstage (or your custom app name)
- `Authorized JavaScript origins`: http://localhost:3000
- `Authorized Redirect URIs`:
- http://localhost:7000/api/auth/google/handler/frame
+ http://localhost:7007/api/auth/google/handler/frame
7. Click Create
## Configuration
diff --git a/docs/auth/index.md b/docs/auth/index.md
index 0c03e33900..4a8d225048 100644
--- a/docs/auth/index.md
+++ b/docs/auth/index.md
@@ -16,8 +16,10 @@ Backstage identity information in your app or plugins.
Backstage comes with many common authentication providers in the core library:
+- [Atlassian](atlassian/provider.md)
- [Auth0](auth0/provider.md)
- [Azure](microsoft/provider.md)
+- [Bitbucket](bitbucket/provider.md)
- [GitHub](github/provider.md)
- [GitLab](gitlab/provider.md)
- [Google](google/provider.md)
diff --git a/docs/auth/microsoft/provider.md b/docs/auth/microsoft/provider.md
index 1b81ff76f1..1e24235f1a 100644
--- a/docs/auth/microsoft/provider.md
+++ b/docs/auth/microsoft/provider.md
@@ -21,7 +21,7 @@ To support Azure authentication, you must create an App Registration:
4. Register an application
- Name: Backstage (or your custom app name)
- Redirect URI: Web >
- `http://localhost:7000/api/auth/microsoft/handler/frame`
+ `http://localhost:7007/api/auth/microsoft/handler/frame`
5. Navigate to **Certificates & secrets > New client secret** to create a secret
## Configuration
diff --git a/docs/auth/okta/provider.md b/docs/auth/okta/provider.md
index b5aaabe4f1..35394094f8 100644
--- a/docs/auth/okta/provider.md
+++ b/docs/auth/okta/provider.md
@@ -22,8 +22,8 @@ To add Okta authentication, you must create an Application from Okta:
- `App integration name`: `Backstage` (or your custom app name)
- `Grant type`: `Authorization Code` & `Refresh Token`
- `Sign-in redirect URIs`:
- `http://localhost:7000/api/auth/okta/handler/frame`
- - `Sign-out redirect URIs`: `http://localhost:7000`
+ `http://localhost:7007/api/auth/okta/handler/frame`
+ - `Sign-out redirect URIs`: `http://localhost:7007`
- `Controlled access`: (select as appropriate)
- Click Save
diff --git a/docs/auth/onelogin/provider.md b/docs/auth/onelogin/provider.md
index a11304ae84..c3572367d2 100644
--- a/docs/auth/onelogin/provider.md
+++ b/docs/auth/onelogin/provider.md
@@ -18,7 +18,7 @@ To support OneLogin authentication, you must create an Application:
3. Click Save
4. Go to the Configuration tab for the Application and set:
- `Login Url`: `http://localhost:3000`
- - `Redirect URIs`: `http://localhost:7000/api/auth/onelogin/handler/frame`
+ - `Redirect URIs`: `http://localhost:7007/api/auth/onelogin/handler/frame`
5. Click Save
6. Go to the SSO tab for the Application and set:
- `Token Endpoint` > `Authentication Method`: `POST`
diff --git a/docs/conf/writing.md b/docs/conf/writing.md
index a0e29820ed..7945d6c980 100644
--- a/docs/conf/writing.md
+++ b/docs/conf/writing.md
@@ -16,8 +16,8 @@ app:
baseUrl: http://localhost:3000
backend:
- listen: 0.0.0.0:7000
- baseUrl: http://localhost:7000
+ listen: 0.0.0.0:7007
+ baseUrl: http://localhost:7007
organization:
name: CNCF
diff --git a/docs/deployment/docker.md b/docs/deployment/docker.md
index 7230f5cf16..6f58abe769 100644
--- a/docs/deployment/docker.md
+++ b/docs/deployment/docker.md
@@ -7,9 +7,9 @@ description: How to build a Backstage Docker image for deployment
This section describes how to build a Backstage App into a deployable Docker
image. It is split into three sections, first covering the host build approach,
-which is recommended due its speed and more efficient and often simpler caching.
-The second section covers a full multi-stage Docker build, and the last section
-covers how to deploy the frontend and backend as separate images.
+which is recommended due to its speed and more efficient and often simpler
+caching. The second section covers a full multi-stage Docker build, and the last
+section covers how to deploy the frontend and backend as separate images.
Something that goes for all of these docker deployment strategies is that they
are stateless, so for a production deployment you will want to set up and
@@ -105,11 +105,11 @@ docker image build . -f packages/backend/Dockerfile --tag backstage
To try out the image locally you can run the following:
```sh
-docker run -it -p 7000:7000 backstage
+docker run -it -p 7007:7007 backstage
```
You should then start to get logs in your terminal, and then you can open your
-browser at `http://localhost:7000`
+browser at `http://localhost:7007`
## Multi-stage Build
@@ -208,11 +208,11 @@ docker image build -t backstage .
To try out the image locally you can run the following:
```sh
-docker run -it -p 7000:7000 backstage
+docker run -it -p 7007:7007 backstage
```
You should then start to get logs in your terminal, and then you can open your
-browser at `http://localhost:7000`
+browser at `http://localhost:7007`
## Separate Frontend
diff --git a/docs/deployment/k8s.md b/docs/deployment/k8s.md
index 2c52e0e308..a80ba1816e 100644
--- a/docs/deployment/k8s.md
+++ b/docs/deployment/k8s.md
@@ -351,7 +351,7 @@ spec:
imagePullPolicy: IfNotPresent
ports:
- name: http
- containerPort: 7000
+ containerPort: 7007
envFrom:
- secretRef:
name: postgres-secrets
@@ -361,11 +361,11 @@ spec:
# https://backstage.io/docs/plugins/observability#health-checks
# readinessProbe:
# httpGet:
-# port: 7000
+# port: 7007
# path: /healthcheck
# livenessProbe:
# httpGet:
-# port: 7000
+# port: 7007
# path: /healthcheck
```
@@ -449,7 +449,7 @@ spec:
```
The `selector` here is telling the Service which pods to target, and the port
-mapping translates normal HTTP port 80 to the backend http port (7000) on the
+mapping translates normal HTTP port 80 to the backend http port (7007) on the
pod.
Apply this Service to the Kubernetes cluster:
@@ -464,10 +464,10 @@ reveal**_, you can forward a local port to the service:
```shell
$ sudo kubectl port-forward --namespace=backstage svc/backstage 80:80
-Forwarding from 127.0.0.1:80 -> 7000
+Forwarding from 127.0.0.1:80 -> 7007
```
-This shows port 7000 since `port-forward` doesn't _really_ support services, so
+This shows port 7007 since `port-forward` doesn't _really_ support services, so
it cheats by looking up the first pod for a service and connecting to the mapped
pod port.
@@ -486,7 +486,7 @@ organization:
backend:
baseUrl: http://localhost
listen:
- port: 7000
+ port: 7007
cors:
origin: http://localhost
```
diff --git a/docs/features/kubernetes/configuration.md b/docs/features/kubernetes/configuration.md
index 6b3991144b..07a12f54a0 100644
--- a/docs/features/kubernetes/configuration.md
+++ b/docs/features/kubernetes/configuration.md
@@ -26,6 +26,7 @@ kubernetes:
name: minikube
authProvider: 'serviceAccount'
skipTLSVerify: false
+ skipMetricsLookup: true
serviceAccountToken: ${K8S_MINIKUBE_TOKEN}
dashboardUrl: http://127.0.0.1:64713 # url copied from running the command: minikube service kubernetes-dashboard -n kubernetes-dashboard
dashboardApp: standard
@@ -37,6 +38,7 @@ kubernetes:
projectId: 'gke-clusters'
region: 'europe-west1'
skipTLSVerify: true
+ skipMetricsLookup: true
```
### `serviceLocatorMethod`
@@ -86,8 +88,13 @@ cluster. Valid values are:
##### `clusters.\*.skipTLSVerify`
-This determines whether or not the Kubernetes client verifies the TLS
-certificate presented by the API server. Defaults to `false`.
+This determines whether the Kubernetes client verifies the TLS certificate
+presented by the API server. Defaults to `false`.
+
+##### `clusters.\*.skipMetricsLookup`
+
+This determines whether the Kubernetes client looks up resource metrics
+CPU/Memory for pods returned by the API server. Defaults to `false`.
##### `clusters.\*.serviceAccountToken` (optional)
@@ -188,8 +195,13 @@ regions.
##### `skipTLSVerify`
-This determines whether or not the Kubernetes client verifies the TLS
-certificate presented by the API server. Defaults to `false`.
+This determines whether the Kubernetes client verifies the TLS certificate
+presented by the API server. Defaults to `false`.
+
+##### `skipMetricsLookup`
+
+This determines whether the Kubernetes client looks up resource metrics
+CPU/Memory for pods returned by the API server. Defaults to `false`.
### `customResources` (optional)
@@ -219,6 +231,26 @@ The custom resource's apiVersion.
The plural representing the custom resource.
+### `apiVersionOverrides` (optional)
+
+Overrides for the API versions used to make requests for the corresponding
+objects. If using a legacy Kubernetes version, you may use this config to
+override the default API versions to ones that are supported by your cluster.
+
+Example:
+
+```yaml
+---
+kubernetes:
+ apiVersionOverrides:
+ cronjobs: 'v1beta1'
+```
+
+For more information on which API versions are supported by your cluster, please
+view the Kubernetes API docs for your Kubernetes version (e.g.
+[API Groups for v1.22](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#-strong-api-groups-strong-)
+)
+
### Role Based Access Control
The current RBAC permissions required are read-only cluster wide, for the
diff --git a/docs/features/search/getting-started.md b/docs/features/search/getting-started.md
index 27219e529a..285e399cb4 100644
--- a/docs/features/search/getting-started.md
+++ b/docs/features/search/getting-started.md
@@ -114,13 +114,15 @@ const routes = (
In `Root.tsx`, add the `SidebarSearchModal` component:
```bash
-import { SidebarSearchModal } from '@backstage/plugin-search';
+import { SidebarSearchModal, SearchContextProvider } from '@backstage/plugin-search';
export const Root = ({ children }: PropsWithChildren<{}>) => (
-
+
+
+
...
```
@@ -154,13 +156,17 @@ import { DefaultCatalogCollator } from '@backstage/plugin-catalog-backend';
export default async function createPlugin({
logger,
discovery,
+ tokenManager,
}: PluginEnvironment) {
const searchEngine = new LunrSearchEngine({ logger });
const indexBuilder = new IndexBuilder({ logger, searchEngine });
indexBuilder.addCollator({
defaultRefreshIntervalSeconds: 600,
- collator: new DefaultCatalogCollator({ discovery }),
+ collator: new DefaultCatalogCollator({
+ discovery,
+ tokenManager,
+ }),
});
const { scheduler } = await indexBuilder.build();
@@ -285,7 +291,10 @@ const indexBuilder = new IndexBuilder({ logger, searchEngine });
indexBuilder.addCollator({
defaultRefreshIntervalSeconds: 600,
- collator: new DefaultCatalogCollator({ discovery }),
+ collator: new DefaultCatalogCollator({
+ discovery,
+ tokenManager,
+ }),
});
indexBuilder.addCollator({
@@ -303,6 +312,9 @@ its `defaultRefreshIntervalSeconds` value, like this:
```typescript {3}
indexBuilder.addCollator({
defaultRefreshIntervalSeconds: 600,
- collator: new DefaultCatalogCollator({ discovery }),
+ collator: new DefaultCatalogCollator({
+ discovery,
+ tokenManager,
+ }),
});
```
diff --git a/docs/features/search/how-to-guides.md b/docs/features/search/how-to-guides.md
index 621d46b51e..4ba44b8ef4 100644
--- a/docs/features/search/how-to-guides.md
+++ b/docs/features/search/how-to-guides.md
@@ -2,7 +2,7 @@
id: how-to-guides
title: Search "HOW TO" guides
sidebar_label: "HOW TO" guides
-description: Search "HOW TO" guides
+description: Search "HOW TO" guides
---
## How to implement your own Search API
@@ -74,6 +74,7 @@ indexBuilder.addCollator({
collator: DefaultTechDocsCollator.fromConfig(config, {
discovery,
logger,
+ tokenManager,
}),
});
```
diff --git a/docs/features/search/search-engines.md b/docs/features/search/search-engines.md
index a0302ce279..1331c73b72 100644
--- a/docs/features/search/search-engines.md
+++ b/docs/features/search/search-engines.md
@@ -4,9 +4,9 @@ title: Search Engines
description: Choosing and configuring your search engine for Backstage
---
-Backstage supports 2 search engines by default, an in-memory engine called Lunr
-and ElasticSearch. You can configure your own search engines by implementing the
-provided interface as mentioned in the
+Backstage supports 3 search engines by default, an in-memory engine called Lunr,
+ElasticSearch and Postgres. You can configure your own search engines by
+implementing the provided interface as mentioned in the
[search backend documentation.](./getting-started.md#Backend)
Provided search engine implementations have their own way of constructing
diff --git a/docs/features/software-templates/configuration.md b/docs/features/software-templates/configuration.md
index 737f77f67b..b57bb96818 100644
--- a/docs/features/software-templates/configuration.md
+++ b/docs/features/software-templates/configuration.md
@@ -53,3 +53,30 @@ You can do so by including the following lines in the last step of your
RUN apt-get update && apt-get install -y python3 python3-pip
RUN pip3 install cookiecutter
```
+
+### Customizing the ScaffolderPage with Grouping and Filtering
+
+Once you have more than a few software templates you may want to customize your
+`ScaffolderPage` by grouping and surfacing certain templates together. You can
+accomplish this by creating `groups` and passing them to your `ScaffolderPage`
+like below
+
+```
+
+ entity?.metadata?.tags?.includes('recommended') ?? false,
+ },
+ ]}
+/>
+```
+
+This code will group all templates with the 'recommended' tag together at the
+top of the page above any other templates not filtered by this group or others.
+
+You can also further customize groups by passing in a `titleComponent` instead
+of a `title` which will be a component to use as the header instead of just the
+default `ContentHeader` with the `title` set as it's value.
+
diff --git a/docs/features/software-templates/index.md b/docs/features/software-templates/index.md
index 1434d62a29..a854543acb 100644
--- a/docs/features/software-templates/index.md
+++ b/docs/features/software-templates/index.md
@@ -12,7 +12,7 @@ code, template in some variables, and then publish the template to some
locations like GitHub or GitLab.
### Getting Started
diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md
index 07c4891a6c..a564200bfe 100644
--- a/docs/features/software-templates/writing-custom-actions.md
+++ b/docs/features/software-templates/writing-custom-actions.md
@@ -8,6 +8,10 @@ If you're wanting to extend the functionality of the Scaffolder, you can do so
by writing custom actions which can be used along side our
[built-in actions](./builtin-actions.md).
+> Note: When adding custom actions, the actions array will **replace the
+> built-in actions too**. To ensure you can continue to include he builtin
+> actions, see below to include them during registration of your action.
+
### Writing your Custom Action
Your custom action can live where you choose, but simplest is to include it
@@ -91,6 +95,8 @@ argument. It looks like the following:
- `createTemporaryDirectory` a function to call to give you a temporary
directory somewhere on the runner so you can store some files there rather
than polluting the `workspacePath`
+- `ctx.metadata` - an object containing a `name` field, indicating the template
+ name. More metadata fields may be added later.
### Registering Custom Actions
@@ -113,22 +119,6 @@ return await createRouter({
There's another property you can pass here, which is an array of `actions` which
will set the available actions that the scaffolder has access to.
-```ts
-const actions = [createNewFileAction()];
-return await createRouter({
- containerRunner,
- logger,
- config,
- database,
- catalogClient,
- reader,
- actions,
-});
-```
-
-**NOTE** - the actions array will replace the built-in actions too, so if you
-want to have those as well as your new one, you'll need to do the following:
-
```ts
import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend';
import { ScmIntegrations } from '@backstage/integration';
@@ -144,7 +134,6 @@ const builtInActions = createBuiltinActions({
});
const actions = [...builtInActions, createNewFileAction()];
-
return await createRouter({
containerRunner,
logger,
diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md
index f95177e029..81f2622312 100644
--- a/docs/features/software-templates/writing-templates.md
+++ b/docs/features/software-templates/writing-templates.md
@@ -284,8 +284,8 @@ to publish to. And it can be any host that is listed in your `integrations`
config in `app-config.yaml`.
The `RepoUrlPicker` is a custom field that we provide part of the
-`plugin-scaffolder`. It's currently not possible to create your own fields yet,
-but contributions are welcome! :)
+`plugin-scaffolder`. You can provide your own custom fields by
+[writing your own Custom Field Extensions](./writing-custom-field-extensions.md)
#### The Owner Picker
diff --git a/docs/features/techdocs/configuration.md b/docs/features/techdocs/configuration.md
index 61d95f55c0..4148749a62 100644
--- a/docs/features/techdocs/configuration.md
+++ b/docs/features/techdocs/configuration.md
@@ -138,11 +138,11 @@ techdocs:
# (Optional and Legacy) TechDocs makes API calls to techdocs-backend using this URL. e.g. get docs of an entity, get metadata, etc.
# You don't have to specify this anymore.
- requestUrl: http://localhost:7000/api/techdocs
+ requestUrl: http://localhost:7007/api/techdocs
# (Optional and Legacy) Just another route in techdocs-backend where TechDocs requests the static files from. This URL uses an HTTP middleware
# to serve files from either a local directory or an External storage provider.
# You don't have to specify this anymore.
- storageUrl: http://localhost:7000/api/techdocs/static/docs
+ storageUrl: http://localhost:7007/api/techdocs/static/docs
```
diff --git a/docs/getting-started/contributors.md b/docs/getting-started/contributors.md
index 28b062b5ca..9154eae4d1 100644
--- a/docs/getting-started/contributors.md
+++ b/docs/getting-started/contributors.md
@@ -43,7 +43,7 @@ the project root. Make sure you have run the above mentioned commands first.
$ yarn dev
```
-This is going to start two things, the frontend (:3000) and the backend (:7000).
+This is going to start two things, the frontend (:3000) and the backend (:7007).
This should open a local instance of Backstage in your browser, otherwise open
one of the URLs printed in the terminal.
diff --git a/docs/getting-started/create-an-app.md b/docs/getting-started/create-an-app.md
index e5d15d5845..1d65f25d1b 100644
--- a/docs/getting-started/create-an-app.md
+++ b/docs/getting-started/create-an-app.md
@@ -36,7 +36,7 @@ This will create a new Backstage App inside the current folder. The name of the
app-folder is the name that was provided when prompted.
-
+
Inside that directory, it will generate all the files and folder structure
diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md
index 9632be71bf..42b334c62b 100644
--- a/docs/getting-started/index.md
+++ b/docs/getting-started/index.md
@@ -23,7 +23,7 @@ guide to do a repository-based installation.
- Access to a Linux-based operating system, such as Linux, MacOS or
[Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/)
-- An account with elevated rights
+- An account with elevated rights to install the dependencies
- `curl` or `wget` installed
- Node.js Active LTS Release installed (currently v14) using one of these
methods:
@@ -36,15 +36,16 @@ guide to do a repository-based installation.
- `yarn` [Installation](https://classic.yarnpkg.com/en/docs/install)
- `docker` [installation](https://docs.docker.com/engine/install/)
- `git` [installation](https://github.com/git-guides/install-git)
-- If the system is not directly accessible over your network, the following
- ports need to be opened: 3000, 7000
+- If the system is not directly accessible over your network the following ports
+ need to be opened: 3000, 7007. This is quite uncommon, unless when you're
+ installing in a container, VM or remote system.
### Create your Backstage App
To install the Backstage Standalone app, we make use of `npx`, a tool to run
-Node executables straight from the registry. Running the command below will
-install Backstage. The wizard will create a subdirectory inside your current
-working directory.
+Node executables straight from the registry. This tool is part of your Node.js
+installation. Running the command below will install Backstage. The wizard will
+create a subdirectory inside your current working directory.
```bash
npx @backstage/create-app
@@ -57,7 +58,7 @@ The wizard will ask you
SQLite option.
-
+
### Run the Backstage app
@@ -72,18 +73,27 @@ yarn dev
```
-
+
It might take a little while, but as soon as the message
`[0] webpack compiled successfully` appears, you can open a browser and directly
navigate to your freshly installed Backstage portal at `http://localhost:3000`.
-You can start exploring the demo immediately.
+You can start exploring the demo immediately. Please note that the in-memory
+database will be cleared when you restart the app, so you'll most likely want to
+carry on with the database steps.
-
+
+The most common next steps are to move to a persistent database, configure
+authentication, and add a plugin:
+
+- [Switching from SQLite to PostgresQL](https://backstage.io/docs/tutorials/switching-sqlite-postgres)
+- [Setting up Authentication](https://backstage.io/docs/auth/)
+- [Adding a plugin](https://backstage.io/docs/getting-started/configure-app-with-plugins)
+
Congratulations! That should be it. Let us know how it went:
[on discord](https://discord.gg/EBHEGzX), file issues for any
[feature](https://github.com/backstage/backstage/issues/new?labels=help+wanted&template=feature_template.md)
@@ -93,10 +103,3 @@ or
[bugs](https://github.com/backstage/backstage/issues/new?labels=bug&template=bug_template.md)
you have, and feel free to
[contribute](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md)!
-
-The most common next steps are to configure Backstage, add a plugin and moving
-to a more persistent database:
-
-- [Setting up Authentication](https://backstage.io/docs/auth/)
-- [Switching from SQLite to PostgresQL](https://backstage.io/docs/tutorials/switching-sqlite-postgres)
-- [Adding a plugin](https://backstage.io/docs/getting-started/configure-app-with-plugins)
diff --git a/docs/getting-started/project-structure.md b/docs/getting-started/project-structure.md
index 687743f99d..a0390571b8 100644
--- a/docs/getting-started/project-structure.md
+++ b/docs/getting-started/project-structure.md
@@ -166,13 +166,9 @@ are separated out into their own folder, see further down.
plugin and [techdocs-cli](https://github.com/backstage/techdocs-cli).
- [`test-utils/`](https://github.com/backstage/backstage/tree/master/packages/test-utils) -
- This package contains more general purpose testing facilities for testing a
+ This package contains general purpose testing facilities for testing a
Backstage App or its plugins.
-- [`test-utils-core/`](https://github.com/backstage/backstage/tree/master/packages/test-utils-core) -
- This package contains specific testing facilities used when testing Backstage
- core internals.
-
- [`theme/`](https://github.com/backstage/backstage/tree/master/packages/theme) -
Holds the Backstage Theme.
diff --git a/docs/getting-started/running-backstage-locally.md b/docs/getting-started/running-backstage-locally.md
index 873c26a099..daad7bf195 100644
--- a/docs/getting-started/running-backstage-locally.md
+++ b/docs/getting-started/running-backstage-locally.md
@@ -70,7 +70,7 @@ cd packages/backend
yarn start
```
-That starts up a backend instance on port 7000.
+That starts up a backend instance on port 7007.
In the other window, we will then launch the frontend. This command is run from
the project root, not inside the backend directory.
diff --git a/docs/integrations/azure/discovery.md b/docs/integrations/azure/discovery.md
new file mode 100644
index 0000000000..ad452f47af
--- /dev/null
+++ b/docs/integrations/azure/discovery.md
@@ -0,0 +1,49 @@
+---
+id: discovery
+title: Azure DevOps Discovery
+sidebar_label: Discovery
+# prettier-ignore
+description: Automatically discovering catalog entities from repositories in an Azure DevOps organization
+---
+
+The Azure DevOps integration has a special discovery processor for discovering
+catalog entities within an Azure DevOps. The processor will crawl the Azure
+DevOps organization and register entities matching the configured path. This can
+be useful as an alternative to static locations or manually adding things to the
+catalog.
+
+To use the discovery processor, you'll need a GitHub integration
+[set up](locations.md) with a `AZURE_TOKEN`. Then you can add a location target
+to the catalog configuration:
+
+```yaml
+catalog:
+ locations:
+ # Scan all repositories for a catalog-info.yaml in the root of the default branch
+ - type: azure-discovery
+ target: https://dev.azure.com/myorg/myproject
+ # Or use a custom pattern for a subset of all repositories with default repository
+ - type: azure-discovery
+ target: https://dev.azure.com/myorg/myproject/_git/service-*
+ # Or use a custom file format and location
+ - type: azure-discovery
+ target: https://dev.azure.com/myorg/myproject/_git/*?path=/src/*/catalog-info.yaml
+```
+
+Note the `azure-discovery` type, as this is not a regular `url` processor.
+
+When using a custom pattern, the target is composed of five parts:
+
+- The base instance URL, `https://dev.azure.com` in this case
+- The organization name which is required, `myorg` in this case
+- The project name which is required, `myproject` in this case
+- The repository blob to scan, which accepts \* wildcard tokens and must be
+ added after `_git/`. This can simply be `*` to scan all repositories in the
+ project.
+- The path within each repository to find the catalog YAML file. This will
+ usually be `/catalog-info.yaml`, `/src/*/catalog-info.yaml` or a similar
+ variation for catalog files stored in the root directory of each repository.
+
+_Note:_ the path parameter follows the same rules as the search on Azure DevOps
+web interface. For more details visit the
+[official search documentation](https://docs.microsoft.com/en-us/azure/devops/project/search/get-started-search?view=azure-devops)
diff --git a/docs/local-dev/cli-commands.md b/docs/local-dev/cli-commands.md
index 18fdef892a..318b37f34e 100644
--- a/docs/local-dev/cli-commands.md
+++ b/docs/local-dev/cli-commands.md
@@ -165,6 +165,7 @@ common example being the `migrations` directory.
Usage: backstage-cli backend:build [options]
Options:
+ --minify Minify the generated code
-h, --help display help for command
```
@@ -371,6 +372,7 @@ the monorepo.
Usage: backstage-cli plugin:build [options]
Options:
+ --minify Minify the generated code
-h, --help display help for command
```
diff --git a/docs/openapi/definitions/auth.yaml b/docs/openapi/definitions/auth.yaml
index 411ce69253..032d89523d 100644
--- a/docs/openapi/definitions/auth.yaml
+++ b/docs/openapi/definitions/auth.yaml
@@ -21,7 +21,7 @@ externalDocs:
description: Backstage official documentation
url: https://github.com/backstage/backstage/blob/master/docs/README.md
servers:
- - url: http://localhost:7000/api/auth/
+ - url: http://localhost:7007/api/auth/
tags:
- name: provider
description: List of endpoints per provider
diff --git a/docs/overview/stability-index.md b/docs/overview/stability-index.md
index a79bcd6e11..a79989412d 100644
--- a/docs/overview/stability-index.md
+++ b/docs/overview/stability-index.md
@@ -27,7 +27,7 @@ point building on top of the previous one:
and the new APIs can be used in parallel. This deprecation must have been
released for at least two weeks before the deprecated API is removed in a
minor version bump.
-- **3** - The time limit for the deprecation is 3 months instead of two days.
+- **3** - The time limit for the deprecation is 3 months instead of two weeks.
TL;DR:
@@ -175,15 +175,6 @@ Utilities for writing tests for Backstage plugins and apps.
Stability: `2`
-### `test-utils-core` [GitHub](https://github.com/backstage/backstage/tree/master/packages/test-utils-core/)
-
-Internal testing utilities that are separated out for usage in
-@backstage/core-app-api and @backstage/core-plugin-api. All exports are
-re-exported by @backstage/test-utils. This package should not be depended on
-directly.
-
-Stability: See @backstage/test-utils
-
### `theme` [GitHub](https://github.com/backstage/backstage/tree/master/packages/theme/)
The core Backstage MUI theme along with customization utilities.
diff --git a/docs/plugins/analytics.md b/docs/plugins/analytics.md
index 7adf2650df..ace9a89968 100644
--- a/docs/plugins/analytics.md
+++ b/docs/plugins/analytics.md
@@ -261,10 +261,13 @@ analytics events captured.
Use it like this:
```tsx
-import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
-import { analyticsApiRef } from '@backstage/core-plugin-api';
-import { MockAnalyticsApi, wrapInTestApp } from '@backstage/test-utils';
import { render, fireEvent, waitFor } from '@testing-library/react';
+import { analyticsApiRef } from '@backstage/core-plugin-api';
+import {
+ MockAnalyticsApi,
+ TestApiProvider,
+ wrapInTestApp,
+} from '@backstage/test-utils';
describe('SomeComponent', () => {
it('should capture event on click', () => {
@@ -274,9 +277,9 @@ describe('SomeComponent', () => {
// Render the component being tested
const { getByText } = render(
wrapInTestApp(
-
+
- ,
+ ,
),
);
diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md
index 1d728b76aa..5ff0e5a62f 100644
--- a/docs/plugins/backend-plugin.md
+++ b/docs/plugins/backend-plugin.md
@@ -44,11 +44,11 @@ cd plugins/carmen-backend
yarn start
```
-This will think for a bit, and then say `Listening on :7000`. In a different
+This will think for a bit, and then say `Listening on :7007`. In a different
terminal window, now run
```sh
-curl localhost:7000/carmen/health
+curl localhost:7007/carmen/health
```
This should return `{"status":"ok"}`. Success! Press `Ctrl + c` to kill it
@@ -107,7 +107,7 @@ root), you should be able to fetch data from it.
```sh
# Note the extra /api here
-curl localhost:7000/api/carmen/health
+curl localhost:7007/api/carmen/health
```
This should return `{"status":"ok"}` like before. Success!
diff --git a/docs/tutorials/backend-to-backend-auth.md b/docs/tutorials/backend-to-backend-auth.md
new file mode 100644
index 0000000000..3cb72a4827
--- /dev/null
+++ b/docs/tutorials/backend-to-backend-auth.md
@@ -0,0 +1,68 @@
+---
+id: backend-to-backend-auth
+title: Backend-to-Backend Authentication
+description:
+ Guide for authenticating API requests between Backstage plugin backends
+---
+
+This tutorial describes the steps needed to handle _backend-to-backend
+authentication_, which allows plugin backends to determine whether a given
+request originates from a legitimate Backstage backend by verifying a token
+signed with a shared secret. This system has limited use for now, but will be
+needed to support the upcoming framework for permissions and authorization (see
+[the PRFC on the topic](https://github.com/backstage/backstage/pull/7761) for
+more details).
+
+Backends have no concept of a Backstage identity, so instead they use a token
+generated using a shared key stored in config. You can generate a unique key for
+your app in a terminal, and set the `BACKEND_SECRET` environment variable to the
+resulting value.
+
+```bash
+node -p 'require("crypto").randomBytes(24).toString("base64")'
+```
+
+Requests originating from a backend plugin can be authenticated by decorating
+them with a backend token. Backend tokens can be generated using a
+`TokenManager`, which can be passed to plugin backends via the
+`PluginEnvironment`. The `TokenManager` provided in new Backstage instances
+generated by `create-app` is a stub, which returns empty tokens and accepts any
+input string as valid. To enable backend-to-backend authentication, you'll need
+to instantiate a new one using the secret from your config instead:
+
+```diff
+// packages/backend/src/index.ts
+
+function makeCreateEnv(config: Config) {
+ const root = getRootLogger();
+ const reader = UrlReaders.default({ logger: root, config });
+ const discovery = SingleHostDiscovery.fromConfig(config);
+
+ root.info(`Created UrlReader ${reader}`);
+
+ const cacheManager = CacheManager.fromConfig(config);
+ const databaseManager = DatabaseManager.fromConfig(config);
+- const tokenManager = ServerTokenManager.noop();
++ const tokenManager = ServerTokenManager.fromConfig(config);
+```
+
+With this `tokenManager`, you can then generate a server token for requests:
+
+```typescript
+const { token } = await this.tokenManager.getToken();
+
+const response = await fetch(pluginBackendApiUrl, {
+ method: 'GET',
+ headers: {
+ ...headers,
+ Authorization: `Bearer ${token}`,
+ },
+});
+```
+
+You can use the same `tokenManager` to authenticate tokens supplied on incoming
+requests:
+
+```typescript
+await tokenManager.authenticate(token); // throws if token is invalid
+```
diff --git a/lerna.json b/lerna.json
index 322929db1d..4621689664 100644
--- a/lerna.json
+++ b/lerna.json
@@ -2,5 +2,5 @@
"packages": ["packages/*", "plugins/*"],
"npmClient": "yarn",
"useWorkspaces": true,
- "version": "0.1.0"
+ "version": "0.0.0"
}
diff --git a/microsite/blog/2020-05-22-phase-2-service-catalog.md b/microsite/blog/2020-05-22-phase-2-service-catalog.md
index 520a2a5f10..2df8ce84f3 100644
--- a/microsite/blog/2020-05-22-phase-2-service-catalog.md
+++ b/microsite/blog/2020-05-22-phase-2-service-catalog.md
@@ -1,5 +1,5 @@
---
-title: Starting Phase 2: The Service Catalog
+title: 'Starting Phase 2: The Service Catalog'
author: Stefan Ålund, Spotify
authorURL: http://twitter.com/stalund
authorImageURL: https://pbs.twimg.com/profile_images/121166861/6919c047c0d0edaace78c3009b28e917-user-full-200-130.generated_400x400.jpg
diff --git a/microsite/blog/2020-08-05-announcing-backstage-software-templates.md b/microsite/blog/2020-08-05-announcing-backstage-software-templates.md
index afda20e499..0bf8d7e581 100644
--- a/microsite/blog/2020-08-05-announcing-backstage-software-templates.md
+++ b/microsite/blog/2020-08-05-announcing-backstage-software-templates.md
@@ -7,7 +7,7 @@ authorURL: https://twitter.com/stalund
**TL;DR** Today we are announcing a new Backstage feature: Software Templates. Simplify setup, standardize tooling, and deploy with the click of a button. Using automated templates, your engineers can spin up a new microservice, website, or other software component with your organization’s best practices built-in, right from the start.
diff --git a/microsite/blog/2020-09-08-announcing-tech-docs.md b/microsite/blog/2020-09-08-announcing-tech-docs.md
index 98fbad57c5..f9d7359526 100644
--- a/microsite/blog/2020-09-08-announcing-tech-docs.md
+++ b/microsite/blog/2020-09-08-announcing-tech-docs.md
@@ -1,5 +1,5 @@
---
-title: Announcing TechDocs: Spotify’s docs-like-code plugin for Backstage
+title: 'Announcing TechDocs: Spotify’s docs-like-code plugin for Backstage'
author: Gary Niemen, Spotify
authorURL: https://github.com/garyniemen
---
diff --git a/microsite/blog/2020-10-22-cost-insights-plugin.md b/microsite/blog/2020-10-22-cost-insights-plugin.md
index d265698694..139e9a7ce9 100644
--- a/microsite/blog/2020-10-22-cost-insights-plugin.md
+++ b/microsite/blog/2020-10-22-cost-insights-plugin.md
@@ -1,5 +1,5 @@
---
-title: New Cost Insights plugin: The engineer’s solution to taming cloud costs
+title: 'New Cost Insights plugin: The engineer’s solution to taming cloud costs'
author: Janisa Anandamohan, Spotify
authorURL: https://twitter.com/janisa_a
---
diff --git a/microsite/blog/2021-01-12-new-backstage-feature-kubernetes-for-service-owners.md b/microsite/blog/2021-01-12-new-backstage-feature-kubernetes-for-service-owners.md
index a0d11a580f..9b8c4637bd 100644
--- a/microsite/blog/2021-01-12-new-backstage-feature-kubernetes-for-service-owners.md
+++ b/microsite/blog/2021-01-12-new-backstage-feature-kubernetes-for-service-owners.md
@@ -1,5 +1,5 @@
---
-title: New Backstage feature: Kubernetes for Service owners
+title: 'New Backstage feature: Kubernetes for Service owners'
author: Matthew Clarke, Spotify
authorURL: https://github.com/mclarke47
---
diff --git a/microsite/blog/2021-06-24-announcing-backstage-search-platform.md b/microsite/blog/2021-06-24-announcing-backstage-search-platform.md
index 0a4ae572c2..6e6c9b7819 100644
--- a/microsite/blog/2021-06-24-announcing-backstage-search-platform.md
+++ b/microsite/blog/2021-06-24-announcing-backstage-search-platform.md
@@ -1,5 +1,5 @@
---
-title: Announcing the Backstage Search platform: a customizable search tool built just for you
+title: 'Announcing the Backstage Search platform: a customizable search tool built just for you'
author: Emma Indal, Spotify
authorURL: https://www.linkedin.com/in/emma-indal
---
diff --git a/microsite/data/plugins/splunk-on-call.yaml b/microsite/data/plugins/splunk-on-call.yaml
index 15ceb86a1b..bb0e25111a 100644
--- a/microsite/data/plugins/splunk-on-call.yaml
+++ b/microsite/data/plugins/splunk-on-call.yaml
@@ -1,7 +1,7 @@
---
title: Splunk On-Call
-author: Spotify
-authorUrl: https://github.com/spotify
+author: ayshiff
+authorUrl: https://github.com/ayshiff
category: Monitoring
description: Splunk On-Call offers a simple way to identify incidents and escalation policies.
documentation: https://github.com/backstage/backstage/tree/master/plugins/splunk-on-call
diff --git a/microsite/package.json b/microsite/package.json
index a2a2fd98de..a24de8935d 100644
--- a/microsite/package.json
+++ b/microsite/package.json
@@ -19,7 +19,7 @@
"@spotify/prettier-config": "^12.0.0",
"docusaurus": "^2.0.0-alpha.70",
"js-yaml": "^4.1.0",
- "prettier": "^2.4.1",
+ "prettier": "^2.5.0",
"yarn-lock-check": "^1.0.5"
},
"prettier": "@spotify/prettier-config"
diff --git a/microsite/sidebars.json b/microsite/sidebars.json
index a24d5897ab..89eb282d14 100644
--- a/microsite/sidebars.json
+++ b/microsite/sidebars.json
@@ -125,7 +125,11 @@
{
"type": "subcategory",
"label": "Azure",
- "ids": ["integrations/azure/locations", "integrations/azure/org"]
+ "ids": [
+ "integrations/azure/locations",
+ "integrations/azure/discovery",
+ "integrations/azure/org"
+ ]
},
{
"type": "subcategory",
diff --git a/microsite/yarn.lock b/microsite/yarn.lock
index f3834ac734..9946f4914d 100644
--- a/microsite/yarn.lock
+++ b/microsite/yarn.lock
@@ -1624,15 +1624,6 @@ cli-width@^2.0.0:
resolved "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48"
integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==
-clipboard@^2.0.0:
- version "2.0.6"
- resolved "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376"
- integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==
- dependencies:
- good-listener "^1.2.2"
- select "^1.1.2"
- tiny-emitter "^2.0.0"
-
clone-response@1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
@@ -1687,9 +1678,9 @@ color-name@^1.0.0, color-name@~1.1.4:
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-string@^1.5.4:
- version "1.5.4"
- resolved "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6"
- integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==
+ version "1.6.0"
+ resolved "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz#c3915f61fe267672cb7e1e064c9d692219f6c312"
+ integrity sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==
dependencies:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
@@ -2158,11 +2149,6 @@ delayed-stream@~1.0.0:
resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
-delegate@^3.1.2:
- version "3.2.0"
- resolved "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
- integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
-
depd@~1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
@@ -3097,13 +3083,6 @@ globule@^1.0.0:
lodash "~4.17.10"
minimatch "~3.0.2"
-good-listener@^1.2.2:
- version "1.2.2"
- resolved "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
- integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=
- dependencies:
- delegate "^3.1.2"
-
got@^7.0.0:
version "7.1.0"
resolved "https://registry.npmjs.org/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a"
@@ -3276,9 +3255,9 @@ highlight.js@^9.16.2:
integrity sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA==
hosted-git-info@^2.1.4:
- version "2.8.8"
- resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
- integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
+ version "2.8.9"
+ resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
+ integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
hsl-regex@^1.0.0:
version "1.0.0"
@@ -4815,9 +4794,9 @@ path-key@^2.0.0, path-key@^2.0.1:
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
path-parse@^1.0.6:
- version "1.0.6"
- resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
- integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+ version "1.0.7"
+ resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-to-regexp@0.1.7:
version "0.1.7"
@@ -4850,6 +4829,11 @@ performance-now@^2.1.0:
resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
+picocolors@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f"
+ integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==
+
pify@^2.0.0, pify@^2.2.0, pify@^2.3.0:
version "2.3.0"
resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -5189,13 +5173,12 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0:
integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.23, postcss@^7.0.27, postcss@^7.0.32:
- version "7.0.35"
- resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24"
- integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==
+ version "7.0.39"
+ resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309"
+ integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==
dependencies:
- chalk "^2.4.2"
+ picocolors "^0.2.1"
source-map "^0.6.1"
- supports-color "^6.1.0"
prepend-http@^1.0.1:
version "1.0.4"
@@ -5207,17 +5190,15 @@ prepend-http@^2.0.0:
resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
-prettier@^2.4.1:
- version "2.4.1"
- resolved "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c"
- integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==
+prettier@^2.5.0:
+ version "2.5.0"
+ resolved "https://registry.npmjs.org/prettier/-/prettier-2.5.0.tgz#a6370e2d4594e093270419d9cc47f7670488f893"
+ integrity sha512-FM/zAKgWTxj40rH03VxzIPdXmj39SwSjwG0heUcNFwI+EMZJnY93yAiKXM3dObIKAM5TA88werc8T/EwhB45eg==
prismjs@^1.22.0:
- version "1.23.0"
- resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz#d3b3967f7d72440690497652a9d40ff046067f33"
- integrity sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==
- optionalDependencies:
- clipboard "^2.0.0"
+ version "1.25.0"
+ resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756"
+ integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==
process-nextick-args@~2.0.0:
version "2.0.1"
@@ -5709,11 +5690,6 @@ seek-bzip@^1.0.5:
dependencies:
commander "^2.8.1"
-select@^1.1.2:
- version "1.1.2"
- resolved "https://registry.npmjs.org/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
- integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=
-
semver-regex@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338"
@@ -5766,9 +5742,9 @@ serve-static@1.14.1:
send "0.17.1"
set-getter@^0.1.0:
- version "0.1.0"
- resolved "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376"
- integrity sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=
+ version "0.1.1"
+ resolved "https://registry.npmjs.org/set-getter/-/set-getter-0.1.1.tgz#a3110e1b461d31a9cfc8c5c9ee2e9737ad447102"
+ integrity sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==
dependencies:
to-object-path "^0.3.0"
@@ -6150,13 +6126,6 @@ supports-color@^5.3.0:
dependencies:
has-flag "^3.0.0"
-supports-color@^6.1.0:
- version "6.1.0"
- resolved "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
- integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
- dependencies:
- has-flag "^3.0.0"
-
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
@@ -6250,11 +6219,6 @@ timsort@^0.3.0:
resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
-tiny-emitter@^2.0.0:
- version "2.1.0"
- resolved "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
- integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
-
tiny-lr@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz#9fa547412f238fedb068ee295af8b682c98b2aab"
@@ -6502,9 +6466,9 @@ url-parse-lax@^3.0.0:
prepend-http "^2.0.0"
url-parse@^1.4.3:
- version "1.4.7"
- resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
- integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
+ version "1.5.3"
+ resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862"
+ integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"
diff --git a/mkdocs.yml b/mkdocs.yml
index 48df25afef..2eed377819 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -88,6 +88,7 @@ nav:
- Discovery: 'integrations/aws-s3/discovery.md'
- Azure:
- Locations: 'integrations/azure/locations.md'
+ - Discovery: 'integrations/azure/discovery.md'
- Org Data: 'integrations/azure/org.md'
- Bitbucket:
- Locations: 'integrations/bitbucket/locations.md'
diff --git a/package.json b/package.json
index 16ba8461ac..2d63f92d3a 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,8 @@
"prettier:check": "prettier --check .",
"lerna": "lerna",
"storybook": "yarn workspace storybook start",
+ "snyk:test": "npx snyk test --yarn-workspaces --strict-out-of-sync=false",
+ "snyk:test:package": "yarn snyk:test --include",
"build-storybook": "yarn workspace storybook build-storybook",
"techdocs-cli": "node scripts/techdocs-cli.js",
"techdocs-cli:dev": "cross-env TECHDOCS_CLI_DEV_MODE=true node scripts/techdocs-cli.js",
@@ -61,7 +63,8 @@
"devDependencies": {
"@changesets/cli": "^2.14.0",
"@octokit/rest": "^18.12.0",
- "@spotify/prettier-config": "^11.0.0",
+ "@spotify/prettier-config": "^12.0.0",
+ "@types/node": "^14.14.32",
"@types/webpack": "^5.28.0",
"command-exists": "^1.2.9",
"cross-env": "^7.0.0",
diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md
new file mode 100644
index 0000000000..b2b50ae48b
--- /dev/null
+++ b/packages/app-defaults/CHANGELOG.md
@@ -0,0 +1,10 @@
+# @backstage/app-defaults
+
+## 0.1.1
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/core-components@0.7.4
+ - @backstage/core-plugin-api@0.2.0
+ - @backstage/core-app-api@0.1.21
diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json
index 9438966b3b..695d457455 100644
--- a/packages/app-defaults/package.json
+++ b/packages/app-defaults/package.json
@@ -1,7 +1,7 @@
{
"name": "@backstage/app-defaults",
"description": "Provides the default wiring of a Backstage App",
- "version": "0.1.0",
+ "version": "0.1.1",
"private": false,
"publishConfig": {
"access": "public",
@@ -29,18 +29,18 @@
"clean": "backstage-cli clean"
},
"dependencies": {
- "@backstage/core-components": "^0.7.3",
- "@backstage/core-app-api": "^0.1.20",
- "@backstage/core-plugin-api": "^0.1.13",
- "@backstage/theme": "^0.2.13",
+ "@backstage/core-components": "^0.7.6",
+ "@backstage/core-app-api": "^0.1.24",
+ "@backstage/core-plugin-api": "^0.2.2",
+ "@backstage/theme": "^0.2.14",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"react": "^16.12.0",
"react-router-dom": "6.0.0-beta.0"
},
"devDependencies": {
- "@backstage/cli": "^0.8.2",
- "@backstage/test-utils": "^0.1.21",
+ "@backstage/cli": "^0.10.0",
+ "@backstage/test-utils": "^0.1.22",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
"@types/jest": "^26.0.7",
diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md
index 1dee41af56..2d006791e9 100644
--- a/packages/app/CHANGELOG.md
+++ b/packages/app/CHANGELOG.md
@@ -1,5 +1,70 @@
# example-app
+## 0.2.55
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-azure-devops@0.1.5
+ - @backstage/core-components@0.7.6
+ - @backstage/theme@0.2.14
+ - @backstage/plugin-user-settings@0.3.12
+ - @backstage/plugin-api-docs@0.6.16
+ - @backstage/cli@0.10.0
+ - @backstage/plugin-circleci@0.2.30
+ - @backstage/core-plugin-api@0.2.2
+ - @backstage/plugin-search@0.5.0
+ - @backstage/plugin-tech-insights@0.1.0
+ - @backstage/core-app-api@0.1.24
+ - @backstage/plugin-kubernetes@0.4.22
+ - @backstage/plugin-scaffolder@0.11.13
+ - @backstage/plugin-techdocs@0.12.8
+
+## 0.2.53
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-catalog@0.7.3
+ - @backstage/plugin-cost-insights@0.11.11
+ - @backstage/cli@0.9.0
+ - @backstage/plugin-gcp-projects@0.3.9
+ - @backstage/catalog-model@0.9.7
+ - @backstage/plugin-scaffolder@0.11.11
+ - @backstage/plugin-api-docs@0.6.14
+ - @backstage/plugin-azure-devops@0.1.4
+ - @backstage/plugin-badges@0.2.14
+ - @backstage/plugin-catalog-graph@0.2.2
+ - @backstage/plugin-catalog-import@0.7.4
+ - @backstage/plugin-catalog-react@0.6.4
+ - @backstage/plugin-circleci@0.2.29
+ - @backstage/plugin-cloudbuild@0.2.28
+ - @backstage/plugin-code-coverage@0.1.18
+ - @backstage/plugin-explore@0.3.21
+ - @backstage/plugin-github-actions@0.4.24
+ - @backstage/plugin-home@0.4.6
+ - @backstage/plugin-jenkins@0.5.12
+ - @backstage/plugin-kafka@0.2.21
+ - @backstage/plugin-kubernetes@0.4.20
+ - @backstage/plugin-lighthouse@0.2.30
+ - @backstage/plugin-newrelic@0.3.9
+ - @backstage/plugin-pagerduty@0.3.18
+ - @backstage/plugin-rollbar@0.3.19
+ - @backstage/plugin-search@0.4.18
+ - @backstage/plugin-sentry@0.3.29
+ - @backstage/plugin-tech-radar@0.4.12
+ - @backstage/plugin-techdocs@0.12.6
+ - @backstage/plugin-todo@0.1.15
+ - @backstage/plugin-user-settings@0.3.11
+ - @backstage/core-components@0.7.4
+ - @backstage/core-plugin-api@0.2.0
+ - @backstage/core-app-api@0.1.21
+ - @backstage/plugin-shortcuts@0.1.14
+ - @backstage/app-defaults@0.1.1
+ - @backstage/integration-react@0.1.14
+ - @backstage/plugin-graphiql@0.2.21
+ - @backstage/plugin-org@0.3.28
+
## 0.2.51
### Patch Changes
diff --git a/packages/app/cypress/integration/components/search/SearchPage.js b/packages/app/cypress/integration/components/search/SearchPage.js
index 6a18d16198..11f31cfed3 100644
--- a/packages/app/cypress/integration/components/search/SearchPage.js
+++ b/packages/app/cypress/integration/components/search/SearchPage.js
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-const API_ENDPOINT = 'http://localhost:7000/api/search/query';
+const API_ENDPOINT = 'http://localhost:7007/api/search/query';
describe('SearchPage', () => {
describe('Given a search context with a term, results, and filter values', () => {
diff --git a/packages/app/package.json b/packages/app/package.json
index 9a1fb108f6..36b811c27d 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -1,50 +1,51 @@
{
"name": "example-app",
- "version": "0.2.51",
+ "version": "0.2.55",
"private": true,
"bundled": true,
"dependencies": {
- "@backstage/app-defaults": "^0.1.0",
- "@backstage/catalog-model": "^0.9.5",
- "@backstage/cli": "^0.8.0",
- "@backstage/core-app-api": "^0.1.18",
- "@backstage/core-components": "^0.7.1",
- "@backstage/core-plugin-api": "^0.1.11",
- "@backstage/integration-react": "^0.1.12",
- "@backstage/plugin-api-docs": "^0.6.12",
- "@backstage/plugin-azure-devops": "^0.1.1",
- "@backstage/plugin-badges": "^0.2.13",
- "@backstage/plugin-catalog": "^0.7.2",
- "@backstage/plugin-catalog-graph": "^0.2.1",
- "@backstage/plugin-catalog-import": "^0.7.3",
- "@backstage/plugin-catalog-react": "^0.6.1",
- "@backstage/plugin-circleci": "^0.2.27",
- "@backstage/plugin-cloudbuild": "^0.2.27",
- "@backstage/plugin-code-coverage": "^0.1.15",
- "@backstage/plugin-cost-insights": "^0.11.10",
- "@backstage/plugin-explore": "^0.3.20",
- "@backstage/plugin-gcp-projects": "^0.3.8",
- "@backstage/plugin-github-actions": "^0.4.22",
- "@backstage/plugin-graphiql": "^0.2.20",
- "@backstage/plugin-home": "^0.4.4",
- "@backstage/plugin-jenkins": "^0.5.11",
- "@backstage/plugin-kafka": "^0.2.19",
- "@backstage/plugin-kubernetes": "^0.4.17",
- "@backstage/plugin-lighthouse": "^0.2.29",
- "@backstage/plugin-newrelic": "^0.3.8",
- "@backstage/plugin-org": "^0.3.27",
- "@backstage/plugin-pagerduty": "0.3.17",
- "@backstage/plugin-rollbar": "^0.3.18",
- "@backstage/plugin-scaffolder": "^0.11.8",
- "@backstage/plugin-search": "^0.4.15",
- "@backstage/plugin-sentry": "^0.3.26",
- "@backstage/plugin-shortcuts": "^0.1.12",
- "@backstage/plugin-tech-radar": "^0.4.11",
- "@backstage/plugin-techdocs": "^0.12.3",
- "@backstage/plugin-todo": "^0.1.14",
- "@backstage/plugin-user-settings": "^0.3.10",
+ "@backstage/app-defaults": "^0.1.1",
+ "@backstage/catalog-model": "^0.9.7",
+ "@backstage/cli": "^0.10.0",
+ "@backstage/core-app-api": "^0.1.24",
+ "@backstage/core-components": "^0.7.6",
+ "@backstage/core-plugin-api": "^0.2.2",
+ "@backstage/integration-react": "^0.1.14",
+ "@backstage/plugin-api-docs": "^0.6.16",
+ "@backstage/plugin-azure-devops": "^0.1.5",
+ "@backstage/plugin-badges": "^0.2.14",
+ "@backstage/plugin-catalog": "^0.7.3",
+ "@backstage/plugin-catalog-graph": "^0.2.2",
+ "@backstage/plugin-catalog-import": "^0.7.4",
+ "@backstage/plugin-catalog-react": "^0.6.4",
+ "@backstage/plugin-circleci": "^0.2.30",
+ "@backstage/plugin-cloudbuild": "^0.2.28",
+ "@backstage/plugin-code-coverage": "^0.1.18",
+ "@backstage/plugin-cost-insights": "^0.11.11",
+ "@backstage/plugin-explore": "^0.3.21",
+ "@backstage/plugin-gcp-projects": "^0.3.9",
+ "@backstage/plugin-github-actions": "^0.4.24",
+ "@backstage/plugin-graphiql": "^0.2.21",
+ "@backstage/plugin-home": "^0.4.6",
+ "@backstage/plugin-jenkins": "^0.5.12",
+ "@backstage/plugin-kafka": "^0.2.21",
+ "@backstage/plugin-kubernetes": "^0.4.22",
+ "@backstage/plugin-lighthouse": "^0.2.30",
+ "@backstage/plugin-newrelic": "^0.3.9",
+ "@backstage/plugin-org": "^0.3.28",
+ "@backstage/plugin-pagerduty": "0.3.18",
+ "@backstage/plugin-rollbar": "^0.3.19",
+ "@backstage/plugin-scaffolder": "^0.11.13",
+ "@backstage/plugin-search": "^0.5.0",
+ "@backstage/plugin-sentry": "^0.3.29",
+ "@backstage/plugin-shortcuts": "^0.1.14",
+ "@backstage/plugin-tech-radar": "^0.4.12",
+ "@backstage/plugin-techdocs": "^0.12.8",
+ "@backstage/plugin-todo": "^0.1.15",
+ "@backstage/plugin-user-settings": "^0.3.12",
"@backstage/search-common": "^0.2.0",
- "@backstage/theme": "^0.2.11",
+ "@backstage/plugin-tech-insights": "^0.1.0",
+ "@backstage/theme": "^0.2.14",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
@@ -64,9 +65,9 @@
"zen-observable": "^0.8.15"
},
"devDependencies": {
- "@backstage/test-utils": "^0.1.19",
- "@rjsf/core": "^3.0.0",
- "@testing-library/cypress": "^7.0.1",
+ "@backstage/test-utils": "^0.1.22",
+ "@rjsf/core": "^3.2.1",
+ "@testing-library/cypress": "^8.0.2",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^13.1.8",
diff --git a/packages/app/src/App.test.tsx b/packages/app/src/App.test.tsx
index aa6781ce61..8c7cefeb2c 100644
--- a/packages/app/src/App.test.tsx
+++ b/packages/app/src/App.test.tsx
@@ -27,14 +27,14 @@ describe('App', () => {
data: {
app: {
title: 'Test',
- support: { url: 'http://localhost:7000/support' },
+ support: { url: 'http://localhost:7007/support' },
},
- backend: { baseUrl: 'http://localhost:7000' },
+ backend: { baseUrl: 'http://localhost:7007' },
lighthouse: {
baseUrl: 'http://localhost:3003',
},
techdocs: {
- storageUrl: 'http://localhost:7000/api/techdocs/static/docs',
+ storageUrl: 'http://localhost:7007/api/techdocs/static/docs',
},
},
context: 'test',
diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx
index 401665b62e..7e5d3db926 100644
--- a/packages/app/src/App.tsx
+++ b/packages/app/src/App.tsx
@@ -34,6 +34,7 @@ import {
SignInPage,
} from '@backstage/core-components';
import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs';
+import { AzurePullRequestsPage } from '@backstage/plugin-azure-devops';
import {
CatalogEntityPage,
CatalogIndexPage,
@@ -175,7 +176,20 @@ const routes = (
>
{techDocsPage}
- }>
+
+ entity?.metadata?.tags?.includes('recommended') ?? false,
+ },
+ ]}
+ />
+ }
+ >
@@ -203,6 +217,7 @@ const routes = (
element={}
/>
} />
+ } />
);
diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx
index 87fa8c8470..57f1fa536f 100644
--- a/packages/app/src/components/Root/Root.tsx
+++ b/packages/app/src/components/Root/Root.tsx
@@ -14,6 +14,31 @@
* limitations under the License.
*/
+import React, { useContext, PropsWithChildren } from 'react';
+import { Link, makeStyles } from '@material-ui/core';
+import HomeIcon from '@material-ui/icons/Home';
+import ExtensionIcon from '@material-ui/icons/Extension';
+import RuleIcon from '@material-ui/icons/AssignmentTurnedIn';
+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,
+ UserSettingsSignInAvatar,
+} from '@backstage/plugin-user-settings';
+import {
+ SidebarSearchModal,
+ SearchContextProvider,
+} from '@backstage/plugin-search';
+import { Shortcuts } from '@backstage/plugin-shortcuts';
import {
Sidebar,
sidebarConfig,
@@ -25,28 +50,6 @@ import {
SidebarScrollWrapper,
SidebarSpace,
} from '@backstage/core-components';
-import { GraphiQLIcon } from '@backstage/plugin-graphiql';
-import { SidebarSearchModal } from '@backstage/plugin-search';
-import { Shortcuts } from '@backstage/plugin-shortcuts';
-import {
- Settings as SidebarSettings,
- UserSettingsSignInAvatar,
-} from '@backstage/plugin-user-settings';
-import { Link, makeStyles } from '@material-ui/core';
-import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
-import RuleIcon from '@material-ui/icons/AssignmentTurnedIn';
-import ExtensionIcon from '@material-ui/icons/Extension';
-import HomeIcon from '@material-ui/icons/Home';
-import LayersIcon from '@material-ui/icons/Layers';
-import LibraryBooks from '@material-ui/icons/LibraryBooks';
-import MenuIcon from '@material-ui/icons/Menu';
-import MoneyIcon from '@material-ui/icons/MonetizationOn';
-import MapIcon from '@material-ui/icons/MyLocation';
-import SearchIcon from '@material-ui/icons/Search';
-import React, { PropsWithChildren, useContext } from 'react';
-import { NavLink } from 'react-router-dom';
-import LogoFull from './LogoFull';
-import LogoIcon from './LogoIcon';
const useSidebarLogoStyles = makeStyles({
root: {
@@ -86,7 +89,9 @@ export const Root = ({ children }: PropsWithChildren<{}>) => (
} to="/search">
-
+
+
+ }>
diff --git a/packages/app/src/components/catalog/EntityPage.test.tsx b/packages/app/src/components/catalog/EntityPage.test.tsx
index 089cb9c7cf..a6878cd28b 100644
--- a/packages/app/src/components/catalog/EntityPage.test.tsx
+++ b/packages/app/src/components/catalog/EntityPage.test.tsx
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
import { EntityLayout } from '@backstage/plugin-catalog';
import {
DefaultStarredEntitiesApi,
@@ -22,7 +21,11 @@ import {
starredEntitiesApiRef,
} from '@backstage/plugin-catalog-react';
import { githubActionsApiRef } from '@backstage/plugin-github-actions';
-import { MockStorageApi, renderInTestApp } from '@backstage/test-utils';
+import {
+ MockStorageApi,
+ renderInTestApp,
+ TestApiProvider,
+} from '@backstage/test-utils';
import React from 'react';
import { cicdContent } from './EntityPage';
@@ -45,22 +48,22 @@ describe('EntityPage Test', () => {
const mockedApi = {
listWorkflowRuns: jest.fn().mockResolvedValue([]),
- getWorkflow: jest.fn(),
- getWorkflowRun: jest.fn(),
- reRunWorkflow: jest.fn(),
- listJobsForWorkflowRun: jest.fn(),
- downloadJobLogsForWorkflowRun: jest.fn(),
- } as jest.Mocked;
-
- const apis = ApiRegistry.with(githubActionsApiRef, mockedApi).with(
- starredEntitiesApiRef,
- new DefaultStarredEntitiesApi({ storageApi: MockStorageApi.create() }),
- );
+ };
describe('cicdContent', () => {
it('Should render GitHub Actions View', async () => {
const rendered = await renderInTestApp(
-
+
@@ -68,7 +71,7 @@ describe('EntityPage Test', () => {
- ,
+ ,
);
expect(rendered.getByText('ExampleComponent')).toBeInTheDocument();
diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx
index 3fe027ddad..3b9b381380 100644
--- a/packages/app/src/components/catalog/EntityPage.tsx
+++ b/packages/app/src/components/catalog/EntityPage.tsx
@@ -35,6 +35,7 @@ import {
} from '@backstage/plugin-api-docs';
import {
EntityAzurePipelinesContent,
+ EntityAzurePullRequestsContent,
isAzureDevOpsAvailable,
} from '@backstage/plugin-azure-devops';
import { EntityBadgesDialog } from '@backstage/plugin-badges';
@@ -264,6 +265,18 @@ const errorsContent = (
);
+const pullRequestsContent = (
+
+
+
+
+
+
+
+
+
+);
+
const overviewContent = (
{entityWarningContent}
@@ -368,7 +381,7 @@ const serviceEntityPage = (
-
+ {pullRequestsContent}
@@ -427,7 +440,7 @@ const websiteEntityPage = (
-
+ {pullRequestsContent}
diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md
index f3464917aa..b2bf629bed 100644
--- a/packages/backend-common/CHANGELOG.md
+++ b/packages/backend-common/CHANGELOG.md
@@ -1,5 +1,61 @@
# @backstage/backend-common
+## 0.9.12
+
+### Patch Changes
+
+- 905dd952ac: Create a `TokenManager` interface and `ServerTokenManager` implementation to generate and validate server tokens for authenticated backend-to-backend API requests.
+- 6b500622d5: Move to using node-fetch internally instead of cross-fetch
+- 54989b671d: Fixed a potential crash in the log redaction code
+- Updated dependencies
+ - @backstage/integration@0.6.10
+ - @backstage/config-loader@0.8.1
+
+## 0.9.11
+
+### Patch Changes
+
+- bab752e2b3: Change default port of backend from 7000 to 7007.
+
+ This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start.
+
+ You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values:
+
+ ```
+ backend:
+ listen: 0.0.0.0:7123
+ baseUrl: http://localhost:7123
+ ```
+
+ More information can be found here: https://backstage.io/docs/conf/writing
+
+- Updated dependencies
+ - @backstage/errors@0.1.5
+
+## 0.9.10
+
+### Patch Changes
+
+- d7c1e0e34a: Added the `isDatabaseConflictError` function.
+- e21e3c6102: Bumping minimum requirements for `dockerode` and `testcontainers`
+- 1e99c73c75: Update internal usage of `configLoader.loadConfig` that now returns an object instead of an array of configs.
+- 1daada3a06: Paths can be specified in backend.reading.allow to further restrict allowed targets
+- 7ad9a07b27: Adding config prop `pluginDivisionMode` to allow plugins using the `pg` client to create their own management schemas in the db. This allows `pg` client plugins to work in separate schemas in the same db.
+- 01f74aa878: Add `AbortSignal` support to `UrlReader`
+- a8732a1200: Make sure that server builder `start()` propagates errors (such as failing to bind to the required port) properly and doesn't resolve the promise prematurely.
+
+ After this change, the backend logger will be able to actually capture the error as it happens:
+
+ ```
+ 2021-11-11T10:54:21.334Z backstage info Initializing http server
+ 2021-11-11T10:54:21.335Z backstage error listen EADDRINUSE: address already in use :::7000 code=EADDRINUSE errno=-48 syscall=listen address=:: port=7000
+ ```
+
+- 26b5da1c1a: Do not redact empty or one-character strings. These imply that it's just a test or local dev, and unnecessarily ruin the log output.
+- Updated dependencies
+ - @backstage/config-loader@0.8.0
+ - @backstage/cli-common@0.1.6
+
## 0.9.9
### Patch Changes
diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md
index e3119e78a8..9c66c0573e 100644
--- a/packages/backend-common/api-report.md
+++ b/packages/backend-common/api-report.md
@@ -526,6 +526,20 @@ export type SearchResponseFile = {
content(): Promise;
};
+// @public
+export class ServerTokenManager implements TokenManager {
+ // (undocumented)
+ authenticate(token: string): Promise;
+ // (undocumented)
+ static fromConfig(config: Config): ServerTokenManager;
+ // (undocumented)
+ getToken(): Promise<{
+ token: string;
+ }>;
+ // (undocumented)
+ static noop(): TokenManager;
+}
+
// @public (undocumented)
export type ServiceBuilder = {
loadConfig(config: Config): ServiceBuilder;
@@ -583,6 +597,16 @@ export interface StatusCheckHandlerOptions {
statusCheck?: StatusCheck;
}
+// @public
+export interface TokenManager {
+ // (undocumented)
+ authenticate: (token: string) => Promise;
+ // (undocumented)
+ getToken: () => Promise<{
+ token: string;
+ }>;
+}
+
// @public
export type UrlReader = {
read(url: string): Promise;
diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts
index dfc845f3b2..fe3c7a5bec 100644
--- a/packages/backend-common/config.d.ts
+++ b/packages/backend-common/config.d.ts
@@ -20,6 +20,20 @@ export interface Config {
};
backend: {
+ /** Backend configuration for when request authentication is enabled */
+ auth?: {
+ /** Keys shared by all backends for signing and validating backend tokens. */
+ keys: {
+ /**
+ * Secret for generating tokens. Should be a base64 string, recommended
+ * length is 24 bytes.
+ *
+ * @visibility secret
+ */
+ secret: string;
+ }[];
+ };
+
baseUrl: string; // defined in core, but repeated here without doc
/** Address that the backend should listen to. */
@@ -69,6 +83,25 @@ export interface Config {
* Defaults to true if unspecified.
*/
ensureExists?: boolean;
+ /**
+ * How plugins databases are managed/divided in the provided database instance.
+ *
+ * `database` -> Plugins are each given their own database to manage their schemas/tables.
+ *
+ * `schema` -> Plugins will be given their own schema (in the specified/default database)
+ * to manage their tables.
+ *
+ * NOTE: Currently only supported by the `pg` client.
+ *
+ * @default database
+ */
+ pluginDivisionMode?: 'database' | 'schema';
+ /**
+ * Arbitrary config object to pass to knex when initializing
+ * (https://knexjs.org/#Installation-client). Most notable is the debug
+ * and asyncStackTraces booleans
+ */
+ knexConfig?: object;
/** Plugin specific database configuration and client override */
plugin?: {
[pluginId: string]: {
@@ -84,6 +117,14 @@ export interface Config {
* Defaults to base config if unspecified.
*/
ensureExists?: boolean;
+ /**
+ * Arbitrary config object to pass to knex when initializing
+ * (https://knexjs.org/#Installation-client). Most notable is the
+ * debug and asyncStackTraces booleans.
+ *
+ * This is merged recursively into the base knexConfig
+ */
+ knexConfig?: object;
};
};
};
diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json
index 16ce62c7d7..e46d659175 100644
--- a/packages/backend-common/package.json
+++ b/packages/backend-common/package.json
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-common",
"description": "Common functionality library for Backstage backends",
- "version": "0.9.9",
+ "version": "0.9.12",
"main": "src/index.ts",
"types": "src/index.ts",
"private": false,
@@ -29,31 +29,31 @@
"clean": "backstage-cli clean"
},
"dependencies": {
- "@backstage/cli-common": "^0.1.5",
+ "@backstage/cli-common": "^0.1.6",
"@backstage/config": "^0.1.11",
- "@backstage/config-loader": "^0.7.2",
- "@backstage/errors": "^0.1.4",
- "@backstage/integration": "^0.6.9",
+ "@backstage/config-loader": "^0.8.1",
+ "@backstage/errors": "^0.1.5",
+ "@backstage/integration": "^0.6.10",
"@backstage/types": "^0.1.1",
"@google-cloud/storage": "^5.8.0",
"@lerna/project": "^4.0.0",
"@octokit/rest": "^18.5.3",
"@types/cors": "^2.8.6",
- "@types/dockerode": "^3.2.1",
+ "@types/dockerode": "^3.3.0",
"@types/express": "^4.17.6",
"archiver": "^5.0.2",
"aws-sdk": "^2.840.0",
"compression": "^1.7.4",
"concat-stream": "^2.0.0",
"cors": "^2.8.5",
- "cross-fetch": "^3.0.6",
- "dockerode": "^3.2.1",
+ "dockerode": "^3.3.1",
"express": "^4.17.1",
"express-promise-router": "^4.1.0",
"fs-extra": "9.1.0",
"git-url-parse": "^11.6.0",
"helmet": "^4.0.0",
"isomorphic-git": "^1.8.0",
+ "jose": "^1.27.1",
"keyv": "^4.0.3",
"keyv-memcache": "^1.2.5",
"knex": "^0.95.1",
@@ -63,6 +63,7 @@
"minimist": "^1.2.5",
"morgan": "^1.10.0",
"node-abort-controller": "^3.0.1",
+ "node-fetch": "^2.6.1",
"raw-body": "^2.4.1",
"selfsigned": "^1.10.7",
"stoppable": "^1.1.0",
@@ -80,8 +81,8 @@
}
},
"devDependencies": {
- "@backstage/cli": "^0.8.2",
- "@backstage/test-utils": "^0.1.21",
+ "@backstage/cli": "^0.10.0",
+ "@backstage/test-utils": "^0.1.23",
"@types/archiver": "^5.1.0",
"@types/compression": "^1.7.0",
"@types/concat-stream": "^1.6.0",
diff --git a/packages/backend-common/src/database/DatabaseManager.test.ts b/packages/backend-common/src/database/DatabaseManager.test.ts
index e839123908..1d2ff58b41 100644
--- a/packages/backend-common/src/database/DatabaseManager.test.ts
+++ b/packages/backend-common/src/database/DatabaseManager.test.ts
@@ -15,13 +15,18 @@
*/
import { ConfigReader } from '@backstage/config';
import { omit } from 'lodash';
-import { createDatabaseClient, ensureDatabaseExists } from './connection';
+import {
+ createDatabaseClient,
+ ensureDatabaseExists,
+ ensureSchemaExists,
+} from './connection';
import { DatabaseManager } from './DatabaseManager';
jest.mock('./connection', () => ({
...jest.requireActual('./connection'),
createDatabaseClient: jest.fn(),
ensureDatabaseExists: jest.fn(),
+ ensureSchemaExists: jest.fn(),
}));
describe('DatabaseManager', () => {
@@ -314,5 +319,228 @@ describe('DatabaseManager', () => {
expect.stringContaining('userdbname'),
);
});
+
+ it('plugin sets schema override for pg client', async () => {
+ const overrideConfig = {
+ backend: {
+ database: {
+ client: 'pg',
+ pluginDivisionMode: 'schema',
+ connection: {
+ host: 'localhost',
+ user: 'foo',
+ password: 'bar',
+ database: 'foodb',
+ },
+ },
+ },
+ };
+ const testManager = DatabaseManager.fromConfig(
+ new ConfigReader(overrideConfig),
+ );
+ const pluginId = 'schemaoverride';
+ await testManager.forPlugin(pluginId).getClient();
+
+ const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1);
+ const [baseConfig, overrides] = mockCalls[0];
+
+ expect(baseConfig.get()).toMatchObject({
+ client: 'pg',
+ connection: config.backend.database.connection,
+ });
+
+ expect(overrides).toMatchObject({
+ searchPath: [pluginId],
+ });
+ });
+
+ it('plugin does not provide schema override for non pg client', async () => {
+ const testManager = DatabaseManager.fromConfig(
+ new ConfigReader({
+ backend: {
+ database: {
+ client: 'sqlite3',
+ pluginDivisionMode: 'schema',
+ connection: {
+ host: 'localhost',
+ user: 'foo',
+ password: 'bar',
+ database: 'foodb',
+ },
+ },
+ },
+ }),
+ );
+ const pluginId = 'any-plugin';
+ await testManager.forPlugin(pluginId).getClient();
+
+ const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1);
+ const [baseConfig, overrides] = mockCalls[0];
+
+ expect(baseConfig.get()).toMatchObject({
+ client: 'sqlite3',
+ connection: config.backend.database.connection,
+ });
+
+ expect(overrides).not.toHaveProperty('searchPath');
+ });
+
+ it('plugin does not provide schema override if pluginDivisionMode is set to database', async () => {
+ const testManager = DatabaseManager.fromConfig(
+ new ConfigReader({
+ backend: {
+ database: {
+ client: 'pg',
+ pluginDivisionMode: 'database',
+ connection: 'some-file-path',
+ },
+ },
+ }),
+ );
+
+ const pluginId = 'any-plugin';
+ await testManager.forPlugin(pluginId).getClient();
+
+ const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1);
+ const [_baseConfig, overrides] = mockCalls[0];
+
+ expect(overrides).not.toHaveProperty('searchPath');
+ });
+
+ it('plugin does not provide schema override if pluginDivisionMode is not set', async () => {
+ const testManager = DatabaseManager.fromConfig(
+ new ConfigReader({
+ backend: {
+ database: {
+ client: 'pg',
+ connection: {
+ host: 'localhost',
+ user: 'foo',
+ password: 'bar',
+ database: 'foodb',
+ },
+ },
+ },
+ }),
+ );
+
+ const pluginId = 'schemaoverride';
+ await testManager.forPlugin(pluginId).getClient();
+
+ const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1);
+ const [_baseConfig, overrides] = mockCalls[0];
+
+ expect(overrides).not.toHaveProperty('searchPath');
+ });
+
+ it('pluginDivisionMode ensures that each plugin schema exists', async () => {
+ const testManager = DatabaseManager.fromConfig(
+ new ConfigReader({
+ backend: {
+ database: {
+ client: 'pg',
+ pluginDivisionMode: 'schema',
+ connection: {
+ host: 'localhost',
+ user: 'foo',
+ password: 'bar',
+ database: 'foodb',
+ },
+ },
+ },
+ }),
+ );
+ const pluginId = 'testdbname';
+ await testManager.forPlugin(pluginId).getClient();
+
+ const mockCalls = mocked(ensureSchemaExists).mock.calls;
+ const [_, schemaName] = mockCalls[0];
+
+ expect(schemaName).toEqual('testdbname');
+ });
+
+ it('pluginDivisionMode allows connection overrides for plugins', async () => {
+ const testManager = DatabaseManager.fromConfig(
+ new ConfigReader({
+ backend: {
+ database: {
+ client: 'pg',
+ pluginDivisionMode: 'schema',
+ connection: {
+ host: 'localhost',
+ user: 'foo',
+ password: 'bar',
+ database: 'foodb',
+ },
+ plugin: {
+ testdbname: {
+ connection: {
+ database: 'database_name_overriden',
+ host: 'newhost',
+ },
+ },
+ },
+ },
+ },
+ }),
+ );
+ const pluginId = 'testdbname';
+ await testManager.forPlugin(pluginId).getClient();
+
+ const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1);
+ const [baseConfig, overrides] = mockCalls[0];
+
+ expect(baseConfig.get()).toMatchObject({
+ client: 'pg',
+ connection: {
+ database: 'database_name_overriden',
+ host: 'newhost',
+ user: 'foo',
+ password: 'bar',
+ },
+ });
+ expect(overrides).toHaveProperty('searchPath', ['testdbname']);
+ expect(overrides).toHaveProperty(
+ 'connection.database',
+ 'database_name_overriden',
+ );
+ });
+
+ it('fetches and merges additional knex config', async () => {
+ const testManager = DatabaseManager.fromConfig(
+ new ConfigReader({
+ backend: {
+ database: {
+ client: 'pg',
+ connection: {
+ host: 'localhost',
+ database: 'foodb',
+ },
+ knexConfig: {
+ something: false,
+ },
+ plugin: {
+ testdbname: {
+ knexConfig: {
+ debug: true,
+ },
+ },
+ },
+ },
+ },
+ }),
+ );
+ await testManager.forPlugin('testdbname').getClient();
+
+ const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1);
+ const [baseConfig] = mockCalls[0];
+
+ expect(baseConfig.data).toEqual(
+ expect.objectContaining({
+ debug: true,
+ something: false,
+ }),
+ );
+ });
});
});
diff --git a/packages/backend-common/src/database/DatabaseManager.ts b/packages/backend-common/src/database/DatabaseManager.ts
index f8cdab1413..a3f31e98f2 100644
--- a/packages/backend-common/src/database/DatabaseManager.ts
+++ b/packages/backend-common/src/database/DatabaseManager.ts
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-import { Knex } from 'knex';
-import { omit } from 'lodash';
import { Config, ConfigReader } from '@backstage/config';
import { JsonObject } from '@backstage/types';
+import { Knex } from 'knex';
+import { merge, omit } from 'lodash';
+import { mergeDatabaseConfig } from './config';
import {
createDatabaseClient,
createNameOverride,
+ createSchemaOverride,
ensureDatabaseExists,
+ ensureSchemaExists,
normalizeConnection,
} from './connection';
import { PluginDatabaseManager } from './types';
@@ -80,14 +83,15 @@ export class DatabaseManager {
* Provides the canonical database name for a given plugin.
*
* This method provides the effective database name which is determined using global
- * and plugin specific database config. If no explicit database name is configured,
- * this method will provide a generated name which is the pluginId prefixed with
- * 'backstage_plugin_'.
+ * and plugin specific database config. If no explicit database name is configured
+ * and `pluginDivisionMode` is not `schema`, this method will provide a generated name
+ * which is the pluginId prefixed with 'backstage_plugin_'. If `pluginDivisionMode` is
+ * `schema`, it will fallback to using the default database for the knex instance.
*
* @param pluginId Lookup the database name for given plugin
* @returns String representing the plugin's database name
*/
- private getDatabaseName(pluginId: string): string {
+ private getDatabaseName(pluginId: string): string | undefined {
const connection = this.getConnectionConfig(pluginId);
if (this.getClientType(pluginId).client === 'sqlite3') {
@@ -96,11 +100,16 @@ export class DatabaseManager {
(connection as Knex.Sqlite3ConnectionConfig)?.filename ?? ':memory:'
);
}
+
+ const databaseName = (connection as Knex.ConnectionConfig)?.database;
+
+ // `pluginDivisionMode` as `schema` should use overridden databaseName if supplied or fallback to default knex database
+ if (this.getPluginDivisionModeConfig() === 'schema') {
+ return databaseName;
+ }
+
// all other supported databases should fallback to an auto-prefixed name
- return (
- (connection as Knex.ConnectionConfig)?.database ??
- `${this.prefix}${pluginId}`
- );
+ return databaseName ?? `${this.prefix}${pluginId}`;
}
/**
@@ -129,6 +138,24 @@ export class DatabaseManager {
};
}
+ /**
+ * Provides the knexConfig which should be used for a given plugin.
+ *
+ * @param pluginId Plugin to get the knexConfig for
+ * @returns the merged kexConfig value or undefined if it isn't specified
+ */
+ private getAdditionalKnexConfig(pluginId: string): JsonObject | undefined {
+ const pluginConfig = this.config
+ .getOptionalConfig(`${pluginPath(pluginId)}.knexConfig`)
+ ?.get();
+
+ const baseConfig = this.config
+ .getOptionalConfig('knexConfig')
+ ?.get();
+
+ return merge(baseConfig, pluginConfig);
+ }
+
private getEnsureExistsConfig(pluginId: string): boolean {
const baseConfig = this.config.getOptionalBoolean('ensureExists') ?? true;
return (
@@ -137,13 +164,18 @@ export class DatabaseManager {
);
}
+ private getPluginDivisionModeConfig(): string {
+ return this.config.getOptionalString('pluginDivisionMode') ?? 'database';
+ }
+
/**
* Provides a Knex connection plugin config by combining base and plugin config.
*
* This method provides a baseConfig for a plugin database connector. If the client type
* has not been overridden, the global connection config will be included with plugin
* specific config as the base. Values from the plugin connection take precedence over the
- * base. Base database name is omitted for all supported databases excluding SQLite.
+ * base. Base database name is omitted for all supported databases excluding SQLite unless
+ * `pluginDivisionMode` is set to `schema`.
*/
private getConnectionConfig(
pluginId: string,
@@ -154,10 +186,13 @@ export class DatabaseManager {
this.config.get('connection'),
this.config.getString('client'),
);
- // As databases cannot be shared, the `database` property from the base connection
- // is omitted. SQLite3's `filename` property is an exception as this is used as a
+ // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`. The
+ // `database` property from the base connection is omitted unless `pluginDivisionMode`
+ // is set to `schema`. SQLite3's `filename` property is an exception as this is used as a
// directory elsewhere so we preserve `filename`.
- baseConnection = omit(baseConnection, 'database');
+ if (this.getPluginDivisionModeConfig() !== 'schema') {
+ baseConnection = omit(baseConnection, 'database');
+ }
// get and normalize optional plugin specific database connection
const connection = normalizeConnection(
@@ -183,11 +218,22 @@ export class DatabaseManager {
const { client } = this.getClientType(pluginId);
return {
+ ...this.getAdditionalKnexConfig(pluginId),
client,
connection: this.getConnectionConfig(pluginId),
};
}
+ /**
+ * Provides a partial Knex.Config database schema override for a given plugin.
+ *
+ * @param pluginId Target plugin to get database schema override
+ * @returns Partial Knex.Config with database schema override
+ */
+ private getSchemaOverrides(pluginId: string): Knex.Config | undefined {
+ return createSchemaOverride(this.getClientType(pluginId).client, pluginId);
+ }
+
/**
* Provides a partial Knex.Config database name override for a given plugin.
*
@@ -195,10 +241,10 @@ export class DatabaseManager {
* @returns Partial Knex.Config with database name override
*/
private getDatabaseOverrides(pluginId: string): Knex.Config {
- return createNameOverride(
- this.getClientType(pluginId).client,
- this.getDatabaseName(pluginId),
- );
+ const databaseName = this.getDatabaseName(pluginId);
+ return databaseName
+ ? createNameOverride(this.getClientType(pluginId).client, databaseName)
+ : {};
}
/**
@@ -212,8 +258,8 @@ export class DatabaseManager {
this.getConfigForPlugin(pluginId) as JsonObject,
);
- if (this.getEnsureExistsConfig(pluginId)) {
- const databaseName = this.getDatabaseName(pluginId);
+ const databaseName = this.getDatabaseName(pluginId);
+ if (databaseName && this.getEnsureExistsConfig(pluginId)) {
try {
await ensureDatabaseExists(pluginConfig, databaseName);
} catch (error) {
@@ -223,9 +269,24 @@ export class DatabaseManager {
}
}
- return createDatabaseClient(
- pluginConfig,
+ let schemaOverrides;
+ if (this.getPluginDivisionModeConfig() === 'schema') {
+ try {
+ schemaOverrides = this.getSchemaOverrides(pluginId);
+ await ensureSchemaExists(pluginConfig, pluginId);
+ } catch (error) {
+ throw new Error(
+ `Failed to connect to the database to make sure that schema for plugin '${pluginId}' exists, ${error}`,
+ );
+ }
+ }
+
+ const databaseClientOverrides = mergeDatabaseConfig(
+ {},
this.getDatabaseOverrides(pluginId),
+ schemaOverrides,
);
+
+ return createDatabaseClient(pluginConfig, databaseClientOverrides);
}
}
diff --git a/packages/backend-common/src/database/connection.test.ts b/packages/backend-common/src/database/connection.test.ts
index 869722ddf7..6f02e7b2ed 100644
--- a/packages/backend-common/src/database/connection.test.ts
+++ b/packages/backend-common/src/database/connection.test.ts
@@ -18,8 +18,24 @@ import { ConfigReader } from '@backstage/config';
import {
createDatabaseClient,
createNameOverride,
+ createSchemaOverride,
+ ensureSchemaExists,
parseConnectionString,
} from './connection';
+import { pgConnector } from './connectors';
+
+const mocked = (f: Function) => f as jest.Mock;
+
+jest.mock('./connectors', () => {
+ const connectors = jest.requireActual('./connectors');
+ return {
+ ...connectors,
+ pgConnector: {
+ ...connectors.pgConnector,
+ ensureSchemaExists: jest.fn(),
+ },
+ };
+});
describe('database connection', () => {
describe('createDatabaseClient', () => {
@@ -152,4 +168,63 @@ describe('database connection', () => {
expect(() => parseConnectionString('sqlite://')).toThrow();
});
});
+
+ describe('createSchemaOverride', () => {
+ it('returns Knex config for postgres', () => {
+ expect(createSchemaOverride('pg', 'testpg')).toHaveProperty(
+ 'searchPath',
+ ['testpg'],
+ );
+ });
+
+ it('throws error for sqlite', () => {
+ expect(createSchemaOverride('sqlite3', 'testsqlite')).toBeUndefined();
+ });
+
+ it('returns Knex config for mysql', () => {
+ expect(createSchemaOverride('mysql', 'testmysql')).toBeUndefined();
+ });
+
+ it('throws an error for unknown connection', () => {
+ expect(createSchemaOverride('unknown', 'testname')).toBeUndefined();
+ });
+ });
+
+ describe('ensureSchemaExists', () => {
+ it('returns sucessfully with pg client', async () => {
+ await ensureSchemaExists(
+ new ConfigReader({
+ client: 'pg',
+ schema: 'catalog',
+ connection: 'postgresql://testuser:testpass@acme:5432/userdbname',
+ }),
+ 'catalog',
+ );
+
+ const mockCalls = mocked(
+ pgConnector.ensureSchemaExists as Function,
+ ).mock.calls.splice(-1);
+ const [baseConfig, schemaName] = mockCalls[0];
+
+ expect(baseConfig.get()).toMatchObject({
+ client: 'pg',
+ connection: 'postgresql://testuser:testpass@acme:5432/userdbname',
+ });
+
+ expect(schemaName).toEqual('catalog');
+ });
+
+ it('throws error for non pg client', () => {
+ return expect(
+ ensureSchemaExists(
+ new ConfigReader({
+ client: 'sqlite3',
+ schema: 'catalog',
+ connection: ':memory:',
+ }),
+ 'catalog',
+ ),
+ ).resolves.toBeUndefined();
+ });
+ });
});
diff --git a/packages/backend-common/src/database/connection.ts b/packages/backend-common/src/database/connection.ts
index 1c5cce3718..3308a26573 100644
--- a/packages/backend-common/src/database/connection.ts
+++ b/packages/backend-common/src/database/connection.ts
@@ -82,6 +82,23 @@ export async function ensureDatabaseExists(
);
}
+/**
+ * Ensures that the given schemas all exist, creating them if they do not.
+ *
+ * @public
+ */
+export async function ensureSchemaExists(
+ dbConfig: Config,
+ ...schemas: Array
+): Promise {
+ const client: DatabaseClient = dbConfig.getString('client');
+
+ return await ConnectorMapping[client]?.ensureSchemaExists?.(
+ dbConfig,
+ ...schemas,
+ );
+}
+
/**
* Provides a Knex.Config object with the provided database name for a given client.
*/
@@ -99,6 +116,23 @@ export function createNameOverride(
}
}
+/**
+ * Provides a Knex.Config object with the provided database schema for a given client. Currently only supported by `pg`.
+ */
+export function createSchemaOverride(
+ client: string,
+ name: string,
+): Partial {
+ try {
+ return ConnectorMapping[client]?.createSchemaOverride?.(name);
+ } catch (e) {
+ throw new InputError(
+ `Unable to create database schema override for '${client}' connector`,
+ e,
+ );
+ }
+}
+
/**
* Parses a connection string for a given client and provides a connection config.
*/
diff --git a/packages/backend-common/src/database/connectors/defaultSchemaOverride.test.ts b/packages/backend-common/src/database/connectors/defaultSchemaOverride.test.ts
new file mode 100644
index 0000000000..2db5ecd454
--- /dev/null
+++ b/packages/backend-common/src/database/connectors/defaultSchemaOverride.test.ts
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2021 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import defaultSchemaOverride from './defaultSchemaOverride';
+
+describe('defaultNameOverride()', () => {
+ it('returns a partial knex static connection config with searchPath set to [schemaName]', () => {
+ const schemaName = 'schemaName';
+ expect(defaultSchemaOverride(schemaName)).toHaveProperty('searchPath', [
+ schemaName,
+ ]);
+ });
+});
diff --git a/packages/backend-common/src/database/connectors/defaultSchemaOverride.ts b/packages/backend-common/src/database/connectors/defaultSchemaOverride.ts
new file mode 100644
index 0000000000..62ad8ab8c8
--- /dev/null
+++ b/packages/backend-common/src/database/connectors/defaultSchemaOverride.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2021 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Knex } from 'knex';
+
+/**
+ * Provides a partial knex config with schema name override.
+ *
+ * @param name schema name to get config override for
+ */
+export default function defaultSchemaOverride(
+ name: string,
+): Partial {
+ return {
+ searchPath: [name],
+ };
+}
diff --git a/packages/backend-common/src/database/connectors/postgres.test.ts b/packages/backend-common/src/database/connectors/postgres.test.ts
index ac988a2880..553d019573 100644
--- a/packages/backend-common/src/database/connectors/postgres.test.ts
+++ b/packages/backend-common/src/database/connectors/postgres.test.ts
@@ -76,6 +76,24 @@ describe('postgres', () => {
});
});
+ it('overrides the schema name', () => {
+ const mockConnection = {
+ ...createMockConnection(),
+ schema: 'schemaName',
+ };
+
+ expect(
+ buildPgDatabaseConfig(createConfig(mockConnection), {
+ searchPath: ['schemaName'],
+ }),
+ ).toEqual({
+ client: 'pg',
+ connection: mockConnection,
+ searchPath: ['schemaName'],
+ useNullAsDefault: true,
+ });
+ });
+
it('adds additional config settings', () => {
const mockConnection = createMockConnection();
diff --git a/packages/backend-common/src/database/connectors/postgres.ts b/packages/backend-common/src/database/connectors/postgres.ts
index 62de2e001e..823d50f73f 100644
--- a/packages/backend-common/src/database/connectors/postgres.ts
+++ b/packages/backend-common/src/database/connectors/postgres.ts
@@ -21,6 +21,7 @@ import { ForwardedError } from '@backstage/errors';
import { mergeDatabaseConfig } from '../config';
import { DatabaseConnector } from '../types';
import defaultNameOverride from './defaultNameOverride';
+import defaultSchemaOverride from './defaultSchemaOverride';
/**
* Creates a knex postgres database connection
@@ -135,6 +136,29 @@ export async function ensurePgDatabaseExists(
}
}
+/**
+ * Creates the missing Postgres schema if it does not exist
+ *
+ * @param dbConfig The database config
+ * @param schemas The name of the schemas to create
+ */
+export async function ensurePgSchemaExists(
+ dbConfig: Config,
+ ...schemas: Array
+): Promise {
+ const admin = createPgDatabaseClient(dbConfig);
+
+ try {
+ const ensureSchema = async (database: string) => {
+ await admin.raw(`CREATE SCHEMA IF NOT EXISTS ??`, [database]);
+ };
+
+ await Promise.all(schemas.map(ensureSchema));
+ } finally {
+ await admin.destroy();
+ }
+}
+
/**
* PostgreSQL database connector.
*
@@ -143,6 +167,8 @@ export async function ensurePgDatabaseExists(
export const pgConnector: DatabaseConnector = Object.freeze({
createClient: createPgDatabaseClient,
ensureDatabaseExists: ensurePgDatabaseExists,
+ ensureSchemaExists: ensurePgSchemaExists,
createNameOverride: defaultNameOverride,
+ createSchemaOverride: defaultSchemaOverride,
parseConnectionString: parsePgConnectionString,
});
diff --git a/packages/backend-common/src/database/types.ts b/packages/backend-common/src/database/types.ts
index edcba1e643..e96f86980b 100644
--- a/packages/backend-common/src/database/types.ts
+++ b/packages/backend-common/src/database/types.ts
@@ -45,6 +45,11 @@ export interface DatabaseConnector {
* database name.
*/
createNameOverride(name: string): Partial;
+ /**
+ * createSchemaOverride provides a partial knex config sufficient to override a
+ * PostgreSQL schema name within utilizing the `searchPath` knex configuration.
+ */
+ createSchemaOverride?(name: string): Partial;
/**
* parseConnectionString produces a knex connection config object representing
* a database connection string.
@@ -64,4 +69,16 @@ export interface DatabaseConnector {
dbConfig: Config,
...databases: Array
): Promise;
+
+ /**
+ * ensureSchemaExists performs a side-effect to ensure schema names passed in are
+ * present.
+ *
+ * Calling this function on schemas which already exist should do nothing.
+ * Missing schemas should be created if needed.
+ */
+ ensureSchemaExists?(
+ dbConfig: Config,
+ ...schemas: Array
+ ): Promise;
}
diff --git a/packages/backend-common/src/discovery/SingleHostDiscovery.ts b/packages/backend-common/src/discovery/SingleHostDiscovery.ts
index c1e092706b..2f1e44b293 100644
--- a/packages/backend-common/src/discovery/SingleHostDiscovery.ts
+++ b/packages/backend-common/src/discovery/SingleHostDiscovery.ts
@@ -37,7 +37,7 @@ export class SingleHostDiscovery implements PluginEndpointDiscovery {
* for the internal one.
*
* The basePath defaults to `/api`, meaning the default full internal
- * path for the `catalog` plugin will be `http://localhost:7000/api/catalog`.
+ * path for the `catalog` plugin will be `http://localhost:7007/api/catalog`.
*/
static fromConfig(config: Config, options?: { basePath?: string }) {
const basePath = options?.basePath ?? '/api';
diff --git a/packages/backend-common/src/index.ts b/packages/backend-common/src/index.ts
index c214961a2e..e430ddc1a4 100644
--- a/packages/backend-common/src/index.ts
+++ b/packages/backend-common/src/index.ts
@@ -31,4 +31,5 @@ export * from './paths';
export * from './reading';
export * from './scm';
export * from './service';
+export * from './tokens';
export * from './util';
diff --git a/packages/backend-common/src/logging/rootLogger.ts b/packages/backend-common/src/logging/rootLogger.ts
index 2a4226f88f..12db7d42a1 100644
--- a/packages/backend-common/src/logging/rootLogger.ts
+++ b/packages/backend-common/src/logging/rootLogger.ts
@@ -54,10 +54,13 @@ export function setRootLoggerRedactionList(redactionList: string[]) {
* and replaces them with the corresponding identifier.
*/
function redactLogLine(info: winston.Logform.TransformableInfo) {
- // TODO(hhogg): The logger is created before the config is loaded,
- // because the logger is needed in the config loader. There is a risk of
- // a secret being logged out during the config loading stage.
- if (redactionRegExp) {
+ // TODO(hhogg): The logger is created before the config is loaded, because the
+ // logger is needed in the config loader. There is a risk of a secret being
+ // logged out during the config loading stage.
+ // TODO(freben): Added a check that info.message actually was a string,
+ // because it turned out that this was not necessarily guaranteed.
+ // https://github.com/backstage/backstage/issues/8306
+ if (redactionRegExp && typeof info.message === 'string') {
info.message = info.message.replace(redactionRegExp, '[REDACTED]');
}
diff --git a/packages/backend-common/src/reading/AzureUrlReader.ts b/packages/backend-common/src/reading/AzureUrlReader.ts
index 82d1192b17..37d97b8941 100644
--- a/packages/backend-common/src/reading/AzureUrlReader.ts
+++ b/packages/backend-common/src/reading/AzureUrlReader.ts
@@ -22,7 +22,7 @@ import {
getAzureRequestOptions,
ScmIntegrations,
} from '@backstage/integration';
-import fetch from 'cross-fetch';
+import fetch, { Response } from 'node-fetch';
import { Minimatch } from 'minimatch';
import { Readable } from 'stream';
import { NotFoundError, NotModifiedError } from '@backstage/errors';
@@ -72,7 +72,13 @@ export class AzureUrlReader implements UrlReader {
try {
response = await fetch(builtUrl, {
...getAzureRequestOptions(this.integration.config),
- ...(signal && { signal }),
+ // TODO(freben): The signal cast is there because pre-3.x versions of
+ // node-fetch have a very slightly deviating AbortSignal type signature.
+ // The difference does not affect us in practice however. The cast can
+ // be removed after we support ESM for CLI dependencies and migrate to
+ // version 3 of node-fetch.
+ // https://github.com/backstage/backstage/issues/8242
+ ...(signal && { signal: signal as any }),
});
} catch (e) {
throw new Error(`Unable to read ${url}, ${e}`);
@@ -123,7 +129,13 @@ export class AzureUrlReader implements UrlReader {
...getAzureRequestOptions(this.integration.config, {
Accept: 'application/zip',
}),
- ...(signal && { signal }),
+ // TODO(freben): The signal cast is there because pre-3.x versions of
+ // node-fetch have a very slightly deviating AbortSignal type signature.
+ // The difference does not affect us in practice however. The cast can be
+ // removed after we support ESM for CLI dependencies and migrate to
+ // version 3 of node-fetch.
+ // https://github.com/backstage/backstage/issues/8242
+ ...(signal && { signal: signal as any }),
});
if (!archiveAzureResponse.ok) {
const message = `Failed to read tree from ${url}, ${archiveAzureResponse.status} ${archiveAzureResponse.statusText}`;
diff --git a/packages/backend-common/src/reading/BitbucketUrlReader.ts b/packages/backend-common/src/reading/BitbucketUrlReader.ts
index c1a113a50d..b0e8056370 100644
--- a/packages/backend-common/src/reading/BitbucketUrlReader.ts
+++ b/packages/backend-common/src/reading/BitbucketUrlReader.ts
@@ -23,7 +23,7 @@ import {
getBitbucketRequestOptions,
ScmIntegrations,
} from '@backstage/integration';
-import fetch from 'cross-fetch';
+import fetch, { Response } from 'node-fetch';
import parseGitUrl from 'git-url-parse';
import { trimEnd } from 'lodash';
import { Minimatch } from 'minimatch';
@@ -94,7 +94,13 @@ export class BitbucketUrlReader implements UrlReader {
try {
response = await fetch(bitbucketUrl.toString(), {
...requestOptions,
- ...(signal && { signal }),
+ // TODO(freben): The signal cast is there because pre-3.x versions of
+ // node-fetch have a very slightly deviating AbortSignal type signature.
+ // The difference does not affect us in practice however. The cast can be
+ // removed after we support ESM for CLI dependencies and migrate to
+ // version 3 of node-fetch.
+ // https://github.com/backstage/backstage/issues/8242
+ ...(signal && { signal: signal as any }),
});
} catch (e) {
throw new Error(`Unable to read ${url}, ${e}`);
diff --git a/packages/backend-common/src/reading/FetchUrlReader.ts b/packages/backend-common/src/reading/FetchUrlReader.ts
index d82b1f7f52..7090d26d35 100644
--- a/packages/backend-common/src/reading/FetchUrlReader.ts
+++ b/packages/backend-common/src/reading/FetchUrlReader.ts
@@ -15,7 +15,7 @@
*/
import { NotFoundError, NotModifiedError } from '@backstage/errors';
-import fetch from 'cross-fetch';
+import fetch, { Response } from 'node-fetch';
import {
ReaderFactory,
ReadTreeResponse,
@@ -86,7 +86,13 @@ export class FetchUrlReader implements UrlReader {
headers: {
...(options?.etag && { 'If-None-Match': options.etag }),
},
- signal: options?.signal,
+ // TODO(freben): The signal cast is there because pre-3.x versions of
+ // node-fetch have a very slightly deviating AbortSignal type signature.
+ // The difference does not affect us in practice however. The cast can
+ // be removed after we support ESM for CLI dependencies and migrate to
+ // version 3 of node-fetch.
+ // https://github.com/backstage/backstage/issues/8242
+ signal: options?.signal as any,
});
} catch (e) {
throw new Error(`Unable to read ${url}, ${e}`);
diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts
index 755e0f17e6..d0fbf6d8ae 100644
--- a/packages/backend-common/src/reading/GithubUrlReader.ts
+++ b/packages/backend-common/src/reading/GithubUrlReader.ts
@@ -21,7 +21,7 @@ import {
ScmIntegrations,
} from '@backstage/integration';
import { RestEndpointMethodTypes } from '@octokit/rest';
-import fetch from 'cross-fetch';
+import fetch, { RequestInit, Response } from 'node-fetch';
import parseGitUrl from 'git-url-parse';
import { Minimatch } from 'minimatch';
import { Readable } from 'stream';
@@ -110,7 +110,13 @@ export class GithubUrlReader implements UrlReader {
...(options?.etag && { 'If-None-Match': options.etag }),
Accept: 'application/vnd.github.v3.raw',
},
- signal: options?.signal,
+ // TODO(freben): The signal cast is there because pre-3.x versions of
+ // node-fetch have a very slightly deviating AbortSignal type signature.
+ // The difference does not affect us in practice however. The cast can
+ // be removed after we support ESM for CLI dependencies and migrate to
+ // version 3 of node-fetch.
+ // https://github.com/backstage/backstage/issues/8242
+ signal: options?.signal as any,
});
} catch (e) {
throw new Error(`Unable to read ${url}, ${e}`);
@@ -165,7 +171,13 @@ export class GithubUrlReader implements UrlReader {
repoDetails.repo.archive_url,
commitSha,
filepath,
- { headers, signal: options?.signal },
+ // TODO(freben): The signal cast is there because pre-3.x versions of
+ // node-fetch have a very slightly deviating AbortSignal type signature.
+ // The difference does not affect us in practice however. The cast can be
+ // removed after we support ESM for CLI dependencies and migrate to
+ // version 3 of node-fetch.
+ // https://github.com/backstage/backstage/issues/8242
+ { headers, signal: options?.signal as any },
options,
);
}
@@ -189,7 +201,7 @@ export class GithubUrlReader implements UrlReader {
repoDetails.repo.archive_url,
commitSha,
filepath,
- { headers, signal: options?.signal },
+ { headers, signal: options?.signal as any },
);
return { files, etag: commitSha };
diff --git a/packages/backend-common/src/reading/GitlabUrlReader.ts b/packages/backend-common/src/reading/GitlabUrlReader.ts
index 128aed3c85..2444e317a7 100644
--- a/packages/backend-common/src/reading/GitlabUrlReader.ts
+++ b/packages/backend-common/src/reading/GitlabUrlReader.ts
@@ -20,7 +20,7 @@ import {
GitLabIntegration,
ScmIntegrations,
} from '@backstage/integration';
-import fetch from 'cross-fetch';
+import fetch, { Response } from 'node-fetch';
import parseGitUrl from 'git-url-parse';
import { Minimatch } from 'minimatch';
import { Readable } from 'stream';
@@ -76,7 +76,13 @@ export class GitlabUrlReader implements UrlReader {
...getGitLabRequestOptions(this.integration.config).headers,
...(etag && { 'If-None-Match': etag }),
},
- ...(signal && { signal }),
+ // TODO(freben): The signal cast is there because pre-3.x versions of
+ // node-fetch have a very slightly deviating AbortSignal type signature.
+ // The difference does not affect us in practice however. The cast can be
+ // removed after we support ESM for CLI dependencies and migrate to
+ // version 3 of node-fetch.
+ // https://github.com/backstage/backstage/issues/8242
+ ...(signal && { signal: signal as any }),
});
} catch (e) {
throw new Error(`Unable to read ${url}, ${e}`);
@@ -145,7 +151,13 @@ export class GitlabUrlReader implements UrlReader {
).toString(),
{
...getGitLabRequestOptions(this.integration.config),
- ...(signal && { signal }),
+ // TODO(freben): The signal cast is there because pre-3.x versions of
+ // node-fetch have a very slightly deviating AbortSignal type signature.
+ // The difference does not affect us in practice however. The cast can
+ // be removed after we support ESM for CLI dependencies and migrate to
+ // version 3 of node-fetch.
+ // https://github.com/backstage/backstage/issues/8242
+ ...(signal && { signal: signal as any }),
},
);
if (!commitsGitlabResponse.ok) {
@@ -169,7 +181,13 @@ export class GitlabUrlReader implements UrlReader {
)}/repository/archive?sha=${branch}`,
{
...getGitLabRequestOptions(this.integration.config),
- ...(signal && { signal }),
+ // TODO(freben): The signal cast is there because pre-3.x versions of
+ // node-fetch have a very slightly deviating AbortSignal type signature.
+ // The difference does not affect us in practice however. The cast can
+ // be removed after we support ESM for CLI dependencies and migrate to
+ // version 3 of node-fetch.
+ // https://github.com/backstage/backstage/issues/8242
+ ...(signal && { signal: signal as any }),
},
);
if (!archiveGitLabResponse.ok) {
diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts
index 885baba54b..b085cccfd6 100644
--- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts
+++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts
@@ -40,7 +40,7 @@ import {
} from './config';
import { createHttpServer, createHttpsServer } from './hostFactory';
-export const DEFAULT_PORT = 7000;
+export const DEFAULT_PORT = 7007;
// '' is express default, which listens to all interfaces
const DEFAULT_HOST = '';
// taken from the helmet source code - don't seem to be exported
diff --git a/packages/backend-common/src/service/lib/config.ts b/packages/backend-common/src/service/lib/config.ts
index 57b814da63..d69b00f04c 100644
--- a/packages/backend-common/src/service/lib/config.ts
+++ b/packages/backend-common/src/service/lib/config.ts
@@ -63,8 +63,8 @@ type CustomOrigin = (
* @example
* ```json
* {
- * baseUrl: "http://localhost:7000",
- * listen: "0.0.0.0:7000"
+ * baseUrl: "http://localhost:7007",
+ * listen: "0.0.0.0:7007"
* }
* ```
*/
diff --git a/packages/backend-common/src/service/types.ts b/packages/backend-common/src/service/types.ts
index 3765cbcfdd..2ad379f31e 100644
--- a/packages/backend-common/src/service/types.ts
+++ b/packages/backend-common/src/service/types.ts
@@ -34,7 +34,7 @@ export type ServiceBuilder = {
*
* If no port is specified, the service will first look for an environment
* variable named PORT and use that if present, otherwise it picks a default
- * port (7000).
+ * port (7007).
*
* @param port - The port to listen on
*/
diff --git a/packages/backend-common/src/tokens/ServerTokenManager.test.ts b/packages/backend-common/src/tokens/ServerTokenManager.test.ts
new file mode 100644
index 0000000000..fe724a0a4b
--- /dev/null
+++ b/packages/backend-common/src/tokens/ServerTokenManager.test.ts
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2021 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ConfigReader } from '@backstage/config';
+import { TokenManager } from './types';
+import { ServerTokenManager } from './ServerTokenManager';
+
+const emptyConfig = new ConfigReader({});
+const configWithSecret = new ConfigReader({
+ backend: { auth: { keys: [{ secret: 'a-secret-key' }] } },
+});
+
+describe('ServerTokenManager', () => {
+ it('should throw if secret in config does not exist', () => {
+ expect(() => ServerTokenManager.fromConfig(emptyConfig)).toThrowError();
+ });
+
+ describe('getToken', () => {
+ it('should return a token if secret in config exists', async () => {
+ const tokenManager = ServerTokenManager.fromConfig(configWithSecret);
+ expect((await tokenManager.getToken()).token).toBeDefined();
+ });
+
+ it('should return a token string if using a noop TokenManager', async () => {
+ const tokenManager = ServerTokenManager.noop();
+ expect((await tokenManager.getToken()).token).toBeDefined();
+ });
+ });
+
+ describe('authenticate', () => {
+ it('should not throw if token is valid', async () => {
+ const tokenManager = ServerTokenManager.fromConfig(configWithSecret);
+ const { token } = await tokenManager.getToken();
+ await expect(tokenManager.authenticate(token)).resolves.not.toThrow();
+ });
+
+ it('should throw if token is invalid', async () => {
+ const tokenManager = ServerTokenManager.fromConfig(configWithSecret);
+ await expect(
+ tokenManager.authenticate('random-string'),
+ ).rejects.toThrowError(/invalid server token/i);
+ });
+
+ it('should validate server tokens created by a different instance using the same secret', async () => {
+ const tokenManager1 = ServerTokenManager.fromConfig(configWithSecret);
+ const tokenManager2 = ServerTokenManager.fromConfig(configWithSecret);
+
+ const { token } = await tokenManager1.getToken();
+
+ await expect(tokenManager2.authenticate(token)).resolves.not.toThrow();
+ });
+
+ it('should validate server tokens created using any of the secrets', async () => {
+ const tokenManager1 = ServerTokenManager.fromConfig(
+ new ConfigReader({
+ backend: { auth: { keys: [{ secret: 'a1b2c3' }] } },
+ }),
+ );
+ const tokenManager2 = ServerTokenManager.fromConfig(
+ new ConfigReader({
+ backend: { auth: { keys: [{ secret: 'd4e5f6' }] } },
+ }),
+ );
+ const tokenManager3 = ServerTokenManager.fromConfig(
+ new ConfigReader({
+ backend: {
+ auth: { keys: [{ secret: 'a1b2c3' }, { secret: 'd4e5f6' }] },
+ },
+ }),
+ );
+
+ const { token: token1 } = await tokenManager1.getToken();
+ await expect(tokenManager3.authenticate(token1)).resolves.not.toThrow();
+
+ const { token: token2 } = await tokenManager2.getToken();
+ await expect(tokenManager3.authenticate(token2)).resolves.not.toThrow();
+ });
+
+ it('should throw for server tokens created using a different secret', async () => {
+ const tokenManager1 = ServerTokenManager.fromConfig(
+ new ConfigReader({
+ backend: { auth: { keys: [{ secret: 'a1b2c3' }] } },
+ }),
+ );
+ const tokenManager2 = ServerTokenManager.fromConfig(
+ new ConfigReader({
+ backend: { auth: { keys: [{ secret: 'd4e5f6' }] } },
+ }),
+ );
+
+ const { token } = await tokenManager1.getToken();
+
+ await expect(tokenManager2.authenticate(token)).rejects.toThrowError(
+ /invalid server token/i,
+ );
+ });
+
+ it('should throw for server tokens created using a noop TokenManager', async () => {
+ const noopTokenManager = ServerTokenManager.noop();
+ const tokenManager = ServerTokenManager.fromConfig(
+ new ConfigReader({
+ backend: { auth: { keys: [{ secret: 'a1b2c3' }] } },
+ }),
+ );
+
+ const { token } = await noopTokenManager.getToken();
+
+ await expect(tokenManager.authenticate(token)).rejects.toThrowError(
+ /invalid server token/i,
+ );
+ });
+ });
+
+ describe('ServerTokenManager.fromConfig', () => {
+ it('should throw if backend auth configuration is missing', () => {
+ expect(() =>
+ ServerTokenManager.fromConfig(new ConfigReader({})),
+ ).toThrow();
+ });
+
+ it('should throw if no keys are included in the configuration', () => {
+ expect(() =>
+ ServerTokenManager.fromConfig(
+ new ConfigReader({
+ backend: { auth: { keys: [] } },
+ }),
+ ),
+ ).toThrow();
+ });
+
+ it('should throw if any key is missing a secret property', () => {
+ expect(() =>
+ ServerTokenManager.fromConfig(
+ new ConfigReader({
+ backend: {
+ auth: {
+ keys: [{ secret: '123' }, {}, { secret: '789' }],
+ },
+ },
+ }),
+ ),
+ ).toThrow();
+ });
+ });
+
+ describe('ServerTokenManager.noop', () => {
+ let noopTokenManager: TokenManager;
+
+ beforeEach(() => {
+ noopTokenManager = ServerTokenManager.noop();
+ });
+
+ it('should accept tokens it generates', async () => {
+ const { token } = await noopTokenManager.getToken();
+
+ await expect(noopTokenManager.authenticate(token)).resolves.not.toThrow();
+ });
+
+ it('should accept tokens generated by other noop token managers', async () => {
+ const noopTokenManager2 = ServerTokenManager.noop();
+ await expect(
+ noopTokenManager.authenticate(
+ (
+ await noopTokenManager2.getToken()
+ ).token,
+ ),
+ ).resolves.not.toThrow();
+ });
+
+ it('should accept signed tokens', async () => {
+ const tokenManager = ServerTokenManager.fromConfig(configWithSecret);
+ await expect(
+ noopTokenManager.authenticate((await tokenManager.getToken()).token),
+ ).resolves.not.toThrow();
+ });
+ });
+});
diff --git a/packages/backend-common/src/tokens/ServerTokenManager.ts b/packages/backend-common/src/tokens/ServerTokenManager.ts
new file mode 100644
index 0000000000..35a07509b6
--- /dev/null
+++ b/packages/backend-common/src/tokens/ServerTokenManager.ts
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { JWKS, JWK, JWT } from 'jose';
+import { Config } from '@backstage/config';
+import { AuthenticationError } from '@backstage/errors';
+import { TokenManager } from './types';
+
+class NoopTokenManager implements TokenManager {
+ async getToken() {
+ return { token: '' };
+ }
+
+ async authenticate() {}
+}
+
+/**
+ * Creates and validates tokens for use during backend-to-backend
+ * authentication.
+ *
+ * @public
+ */
+export class ServerTokenManager implements TokenManager {
+ private readonly verificationKeys: JWKS.KeyStore;
+ private readonly signingKey: JWK.Key;
+
+ static noop(): TokenManager {
+ return new NoopTokenManager();
+ }
+
+ static fromConfig(config: Config) {
+ return new ServerTokenManager(
+ config
+ .getConfigArray('backend.auth.keys')
+ .map(key => key.getString('secret')),
+ );
+ }
+
+ private constructor(secrets?: string[]) {
+ if (!secrets?.length) {
+ throw new Error(
+ 'No secrets provided when constructing ServerTokenManager',
+ );
+ }
+
+ this.verificationKeys = new JWKS.KeyStore(
+ secrets.map(k => JWK.asKey({ kty: 'oct', k })),
+ );
+ this.signingKey = this.verificationKeys.all()[0];
+ }
+
+ async getToken(): Promise<{ token: string }> {
+ const jwt = JWT.sign({ sub: 'backstage-server' }, this.signingKey, {
+ algorithm: 'HS256',
+ });
+
+ return { token: jwt };
+ }
+
+ async authenticate(token: string): Promise {
+ try {
+ JWT.verify(token, this.verificationKeys);
+ } catch (e) {
+ throw new AuthenticationError('Invalid server token');
+ }
+ }
+}
diff --git a/packages/backend-common/src/tokens/index.ts b/packages/backend-common/src/tokens/index.ts
new file mode 100644
index 0000000000..43ff12e597
--- /dev/null
+++ b/packages/backend-common/src/tokens/index.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export { ServerTokenManager } from './ServerTokenManager';
+export type { TokenManager } from './types';
diff --git a/packages/backend-common/src/tokens/types.ts b/packages/backend-common/src/tokens/types.ts
new file mode 100644
index 0000000000..1fea018db9
--- /dev/null
+++ b/packages/backend-common/src/tokens/types.ts
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2021 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Interface for creating and validating tokens.
+ *
+ * @public
+ */
+export interface TokenManager {
+ getToken: () => Promise<{ token: string }>;
+ authenticate: (token: string) => Promise;
+}
diff --git a/packages/backend-tasks/package.json b/packages/backend-tasks/package.json
index 10db5cfaac..a22895140e 100644
--- a/packages/backend-tasks/package.json
+++ b/packages/backend-tasks/package.json
@@ -29,7 +29,7 @@
"clean": "backstage-cli clean"
},
"dependencies": {
- "@backstage/backend-common": "^0.9.8",
+ "@backstage/backend-common": "^0.9.12",
"@backstage/config": "^0.1.11",
"@backstage/errors": "^0.1.4",
"@backstage/types": "^0.1.1",
@@ -43,8 +43,8 @@
"zod": "^3.9.5"
},
"devDependencies": {
- "@backstage/backend-test-utils": "^0.1.8",
- "@backstage/cli": "^0.8.1",
+ "@backstage/backend-test-utils": "^0.1.10",
+ "@backstage/cli": "^0.10.0",
"jest": "^26.0.1",
"wait-for-expect": "^3.0.2"
},
diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md
index 287e40a296..584187b9d3 100644
--- a/packages/backend-test-utils/CHANGELOG.md
+++ b/packages/backend-test-utils/CHANGELOG.md
@@ -1,5 +1,22 @@
# @backstage/backend-test-utils
+## 0.1.10
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/backend-common@0.9.12
+ - @backstage/cli@0.10.0
+
+## 0.1.9
+
+### Patch Changes
+
+- e21e3c6102: Bumping minimum requirements for `dockerode` and `testcontainers`
+- Updated dependencies
+ - @backstage/cli@0.9.0
+ - @backstage/backend-common@0.9.10
+
## 0.1.8
### Patch Changes
diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json
index 2f29c2412b..2c1724e50f 100644
--- a/packages/backend-test-utils/package.json
+++ b/packages/backend-test-utils/package.json
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-test-utils",
"description": "Test helpers library for Backstage backends",
- "version": "0.1.8",
+ "version": "0.1.10",
"main": "src/index.ts",
"types": "src/index.ts",
"private": false,
@@ -30,18 +30,18 @@
"clean": "backstage-cli clean"
},
"dependencies": {
- "@backstage/backend-common": "^0.9.7",
- "@backstage/cli": "^0.8.0",
+ "@backstage/backend-common": "^0.9.12",
+ "@backstage/cli": "^0.10.0",
"@backstage/config": "^0.1.9",
"knex": "^0.95.1",
"mysql2": "^2.2.5",
"pg": "^8.3.0",
"sqlite3": "^5.0.1",
- "testcontainers": "^7.10.0",
+ "testcontainers": "^7.23.0",
"uuid": "^8.0.0"
},
"devDependencies": {
- "@backstage/cli": "^0.8.0",
+ "@backstage/cli": "^0.10.0",
"jest": "^26.0.1"
},
"files": [
diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md
index 7cf7d9ddaa..7aa2fb5f55 100644
--- a/packages/backend/CHANGELOG.md
+++ b/packages/backend/CHANGELOG.md
@@ -1,5 +1,49 @@
# example-backend
+## 0.2.55
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/integration@0.6.10
+ - @backstage/plugin-scaffolder-backend@0.15.15
+ - @backstage/plugin-auth-backend@0.4.10
+ - @backstage/plugin-kubernetes-backend@0.3.20
+ - @backstage/plugin-badges-backend@0.1.13
+ - @backstage/plugin-catalog-backend@0.19.0
+ - @backstage/plugin-code-coverage-backend@0.1.16
+ - @backstage/plugin-jenkins-backend@0.1.9
+ - @backstage/plugin-tech-insights-backend@0.1.3
+ - @backstage/plugin-techdocs-backend@0.11.0
+ - @backstage/plugin-todo-backend@0.1.14
+ - @backstage/backend-common@0.9.12
+ - @backstage/plugin-azure-devops-backend@0.2.3
+ - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.2
+ - @backstage/plugin-tech-insights-node@0.1.1
+ - example-app@0.2.55
+
+## 0.2.54
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-kubernetes-backend@0.3.19
+ - @backstage/plugin-tech-insights-backend@0.1.2
+ - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.1
+ - @backstage/plugin-auth-backend@0.4.9
+ - @backstage/plugin-scaffolder-backend@0.15.14
+ - @backstage/plugin-catalog-backend@0.18.0
+ - @backstage/plugin-kafka-backend@0.2.12
+ - @backstage/backend-common@0.9.11
+ - @backstage/plugin-azure-devops-backend@0.2.2
+ - @backstage/plugin-badges-backend@0.1.12
+ - @backstage/plugin-code-coverage-backend@0.1.15
+ - @backstage/plugin-jenkins-backend@0.1.8
+ - @backstage/plugin-proxy-backend@0.2.14
+ - @backstage/plugin-rollbar-backend@0.1.16
+ - @backstage/plugin-search-backend@0.2.7
+ - @backstage/plugin-techdocs-backend@0.10.9
+
## 0.2.52
### Patch Changes
diff --git a/packages/backend/README.md b/packages/backend/README.md
index e6f0c899ca..c2145705e0 100644
--- a/packages/backend/README.md
+++ b/packages/backend/README.md
@@ -37,7 +37,7 @@ Substitute `x` for actual values, or leave them as
dummy values just to try out the backend without using the auth or sentry features.
You can also, instead of using dummy values for a huge number of environment variables, remove those config directly from app-config.yaml file located in the root folder.
-The backend starts up on port 7000 per default.
+The backend starts up on port 7007 per default.
### Debugging
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 4c854241d4..cc8a8ac76a 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -1,6 +1,6 @@
{
"name": "example-backend",
- "version": "0.2.52",
+ "version": "0.2.55",
"main": "dist/index.cjs.js",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -24,39 +24,39 @@
"migrate:create": "knex migrate:make -x ts"
},
"dependencies": {
- "@backstage/backend-common": "^0.9.9",
- "@backstage/catalog-client": "^0.5.1",
- "@backstage/catalog-model": "^0.9.5",
+ "@backstage/backend-common": "^0.9.12",
+ "@backstage/catalog-client": "^0.5.2",
+ "@backstage/catalog-model": "^0.9.7",
"@backstage/config": "^0.1.10",
- "@backstage/integration": "^0.6.8",
- "@backstage/plugin-app-backend": "^0.3.17",
- "@backstage/plugin-auth-backend": "^0.4.7",
- "@backstage/plugin-azure-devops-backend": "^0.2.0",
- "@backstage/plugin-badges-backend": "^0.1.11",
- "@backstage/plugin-catalog-backend": "^0.17.3",
- "@backstage/plugin-code-coverage-backend": "^0.1.14",
+ "@backstage/integration": "^0.6.10",
+ "@backstage/plugin-app-backend": "^0.3.19",
+ "@backstage/plugin-auth-backend": "^0.4.10",
+ "@backstage/plugin-azure-devops-backend": "^0.2.3",
+ "@backstage/plugin-badges-backend": "^0.1.13",
+ "@backstage/plugin-catalog-backend": "^0.19.0",
+ "@backstage/plugin-code-coverage-backend": "^0.1.16",
"@backstage/plugin-graphql-backend": "^0.1.9",
- "@backstage/plugin-jenkins-backend": "^0.1.7",
- "@backstage/plugin-kubernetes-backend": "^0.3.18",
- "@backstage/plugin-kafka-backend": "^0.2.10",
- "@backstage/plugin-proxy-backend": "^0.2.13",
- "@backstage/plugin-rollbar-backend": "^0.1.15",
- "@backstage/plugin-scaffolder-backend": "^0.15.12",
+ "@backstage/plugin-jenkins-backend": "^0.1.9",
+ "@backstage/plugin-kubernetes-backend": "^0.3.20",
+ "@backstage/plugin-kafka-backend": "^0.2.12",
+ "@backstage/plugin-proxy-backend": "^0.2.14",
+ "@backstage/plugin-rollbar-backend": "^0.1.16",
+ "@backstage/plugin-scaffolder-backend": "^0.15.15",
"@backstage/plugin-scaffolder-backend-module-rails": "^0.1.7",
- "@backstage/plugin-search-backend": "^0.2.6",
+ "@backstage/plugin-search-backend": "^0.2.7",
"@backstage/plugin-search-backend-node": "^0.4.2",
"@backstage/plugin-search-backend-module-elasticsearch": "^0.0.5",
"@backstage/plugin-search-backend-module-pg": "^0.2.1",
- "@backstage/plugin-techdocs-backend": "^0.10.5",
- "@backstage/plugin-tech-insights-backend": "^0.1.0",
- "@backstage/plugin-tech-insights-node": "^0.1.0",
- "@backstage/plugin-tech-insights-backend-module-jsonfc": "^0.1.0",
- "@backstage/plugin-todo-backend": "^0.1.13",
- "@gitbeaker/node": "^30.2.0",
+ "@backstage/plugin-techdocs-backend": "^0.11.0",
+ "@backstage/plugin-tech-insights-backend": "^0.1.3",
+ "@backstage/plugin-tech-insights-node": "^0.1.1",
+ "@backstage/plugin-tech-insights-backend-module-jsonfc": "^0.1.2",
+ "@backstage/plugin-todo-backend": "^0.1.14",
+ "@gitbeaker/node": "^34.6.0",
"@octokit/rest": "^18.5.3",
"azure-devops-node-api": "^11.0.1",
- "dockerode": "^3.2.1",
- "example-app": "^0.2.51",
+ "dockerode": "^3.3.1",
+ "example-app": "^0.2.55",
"express": "^4.17.1",
"express-promise-router": "^4.1.0",
"express-prom-bundle": "^6.3.6",
@@ -68,8 +68,8 @@
"winston": "^3.2.1"
},
"devDependencies": {
- "@backstage/cli": "^0.8.2",
- "@types/dockerode": "^3.2.1",
+ "@backstage/cli": "^0.10.0",
+ "@types/dockerode": "^3.3.0",
"@types/express": "^4.17.6",
"@types/express-serve-static-core": "^4.17.5"
},
diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts
index f978e84da9..4426ed7767 100644
--- a/packages/backend/src/index.ts
+++ b/packages/backend/src/index.ts
@@ -33,6 +33,7 @@ import {
SingleHostDiscovery,
UrlReaders,
useHotMemoize,
+ ServerTokenManager,
} from '@backstage/backend-common';
import { Config } from '@backstage/config';
import healthcheck from './plugins/healthcheck';
@@ -60,6 +61,7 @@ function makeCreateEnv(config: Config) {
const root = getRootLogger();
const reader = UrlReaders.default({ logger: root, config });
const discovery = SingleHostDiscovery.fromConfig(config);
+ const tokenManager = ServerTokenManager.noop();
root.info(`Created UrlReader ${reader}`);
@@ -70,7 +72,7 @@ function makeCreateEnv(config: Config) {
const logger = root.child({ type: 'plugin', plugin });
const database = databaseManager.forPlugin(plugin);
const cache = cacheManager.forPlugin(plugin);
- return { logger, cache, database, config, reader, discovery };
+ return { logger, cache, database, config, reader, discovery, tokenManager };
};
}
diff --git a/packages/backend/src/plugins/search.ts b/packages/backend/src/plugins/search.ts
index 4cf9c10021..9a8db0f0f9 100644
--- a/packages/backend/src/plugins/search.ts
+++ b/packages/backend/src/plugins/search.ts
@@ -59,6 +59,7 @@ export default async function createPlugin({
discovery,
config,
database,
+ tokenManager,
}: PluginEnvironment) {
// Initialize a connection to a search engine.
const searchEngine = await createSearchEngine({ config, logger, database });
@@ -68,7 +69,10 @@ export default async function createPlugin({
// particular collator gathers entities from the software catalog.
indexBuilder.addCollator({
defaultRefreshIntervalSeconds: 600,
- collator: DefaultCatalogCollator.fromConfig(config, { discovery }),
+ collator: DefaultCatalogCollator.fromConfig(config, {
+ discovery,
+ tokenManager,
+ }),
});
indexBuilder.addCollator({
@@ -76,6 +80,7 @@ export default async function createPlugin({
collator: DefaultTechDocsCollator.fromConfig(config, {
discovery,
logger,
+ tokenManager,
}),
});
diff --git a/packages/backend/src/plugins/techInsights.ts b/packages/backend/src/plugins/techInsights.ts
index abab1d133d..8ab6342a4f 100644
--- a/packages/backend/src/plugins/techInsights.ts
+++ b/packages/backend/src/plugins/techInsights.ts
@@ -17,10 +17,12 @@ import {
createRouter,
buildTechInsightsContext,
createFactRetrieverRegistration,
+ entityOwnershipFactRetriever,
+ entityMetadataFactRetriever,
+ techdocsFactRetriever,
} from '@backstage/plugin-tech-insights-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
-import { CatalogClient } from '@backstage/catalog-client';
import {
JsonRulesEngineFactCheckerFactory,
JSON_RULE_ENGINE_CHECK_TYPE,
@@ -38,41 +40,12 @@ export default async function createPlugin({
database,
discovery,
factRetrievers: [
- createFactRetrieverRegistration('5 4 * * 6', {
- // Example cron, At 04:05 on Saturday.
- id: 'testRetriever',
- version: '1.1.2',
- entityFilter: [{ kind: 'component' }], // EntityFilter to be used in the future (creating checks, graphs etc.) to figure out which entities this fact retrieves data for.
- schema: {
- examplenumberfact: {
- type: 'integer',
- description: 'Example fact returning a number',
- },
- },
- handler: async _ctx => {
- const catalogClient = new CatalogClient({
- discoveryApi: discovery,
- });
- const entities = await catalogClient.getEntities({
- filter: [{ kind: 'component' }],
- });
-
- return Promise.resolve(
- entities.items.map(it => {
- return {
- entity: {
- namespace: it.metadata.namespace!!,
- kind: it.kind,
- name: it.metadata.name,
- },
- facts: {
- examplenumberfact: 2,
- },
- };
- }),
- );
- },
- }),
+ createFactRetrieverRegistration(
+ '* * * * *', // Example cron, every minute
+ entityOwnershipFactRetriever,
+ ),
+ createFactRetrieverRegistration('* * * * *', entityMetadataFactRetriever),
+ createFactRetrieverRegistration('* * * * *', techdocsFactRetriever),
],
factCheckerFactory: new JsonRulesEngineFactCheckerFactory({
checks: [
@@ -81,14 +54,33 @@ export default async function createPlugin({
type: JSON_RULE_ENGINE_CHECK_TYPE,
name: 'simpleTestCheck',
description: 'Simple Check For Testing',
- factIds: ['testRetriever'],
+ factIds: [
+ 'entityMetadataFactRetriever',
+ 'techdocsFactRetriever',
+ 'entityOwnershipFactRetriever',
+ ],
rule: {
conditions: {
all: [
{
- fact: 'examplenumberfact',
- operator: 'lessThan',
- value: 5,
+ fact: 'hasGroupOwner',
+ operator: 'equal',
+ value: true,
+ },
+ {
+ fact: 'hasTitle',
+ operator: 'equal',
+ value: true,
+ },
+ {
+ fact: 'hasDescription',
+ operator: 'equal',
+ value: true,
+ },
+ {
+ fact: 'hasAnnotationBackstageIoTechdocsRef',
+ operator: 'equal',
+ value: true,
},
],
},
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index 8290e569ef..4be9c036b3 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -20,6 +20,7 @@ import {
PluginCacheManager,
PluginDatabaseManager,
PluginEndpointDiscovery,
+ TokenManager,
UrlReader,
} from '@backstage/backend-common';
@@ -30,4 +31,5 @@ export type PluginEnvironment = {
config: Config;
reader: UrlReader;
discovery: PluginEndpointDiscovery;
+ tokenManager: TokenManager;
};
diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md
index f5ec2116f7..1b11e3bcb2 100644
--- a/packages/catalog-client/CHANGELOG.md
+++ b/packages/catalog-client/CHANGELOG.md
@@ -1,5 +1,13 @@
# @backstage/catalog-client
+## 0.5.2
+
+### Patch Changes
+
+- 3bf2238187: Update to the right version of @backstage/errors
+- Updated dependencies
+ - @backstage/catalog-model@0.9.7
+
## 0.5.1
### Patch Changes
diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json
index 9caac50c90..28a3d5f969 100644
--- a/packages/catalog-client/package.json
+++ b/packages/catalog-client/package.json
@@ -1,7 +1,7 @@
{
"name": "@backstage/catalog-client",
"description": "An isomorphic client for the catalog backend",
- "version": "0.5.1",
+ "version": "0.5.2",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -30,12 +30,12 @@
"clean": "backstage-cli clean"
},
"dependencies": {
- "@backstage/catalog-model": "^0.9.5",
+ "@backstage/catalog-model": "^0.9.7",
"@backstage/errors": "^0.1.4",
"cross-fetch": "^3.0.6"
},
"devDependencies": {
- "@backstage/cli": "^0.8.2",
+ "@backstage/cli": "^0.10.0",
"@types/jest": "^26.0.7",
"msw": "^0.35.0"
},
diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md
index e0c067c9c7..efd6513cdd 100644
--- a/packages/catalog-model/CHANGELOG.md
+++ b/packages/catalog-model/CHANGELOG.md
@@ -1,5 +1,11 @@
# @backstage/catalog-model
+## 0.9.7
+
+### Patch Changes
+
+- 8809b6c0dd: Update the json-schema dependency version.
+
## 0.9.6
### Patch Changes
diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json
index 17c89ed824..60331d9dc8 100644
--- a/packages/catalog-model/package.json
+++ b/packages/catalog-model/package.json
@@ -1,7 +1,7 @@
{
"name": "@backstage/catalog-model",
"description": "Types and validators that help describe the model of a Backstage Catalog",
- "version": "0.9.6",
+ "version": "0.9.7",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -42,7 +42,7 @@
"yup": "^0.32.9"
},
"devDependencies": {
- "@backstage/cli": "^0.8.1",
+ "@backstage/cli": "^0.10.0",
"@types/jest": "^26.0.7",
"@types/lodash": "^4.14.151",
"yaml": "^1.9.2"
diff --git a/packages/cli-common/CHANGELOG.md b/packages/cli-common/CHANGELOG.md
index f3978798c6..e588c02edf 100644
--- a/packages/cli-common/CHANGELOG.md
+++ b/packages/cli-common/CHANGELOG.md
@@ -1,5 +1,13 @@
# @backstage/cli-common
+## 0.1.6
+
+### Patch Changes
+
+- 677bfc2dd0: Keep backstage.json in sync
+
+ The `versions:bump` script now takes care about updating the `version` property inside `backstage.json` file. The file is created if is not present.
+
## 0.1.5
### Patch Changes
diff --git a/packages/cli-common/api-report.md b/packages/cli-common/api-report.md
index 4b61cfedb8..34f56c3f19 100644
--- a/packages/cli-common/api-report.md
+++ b/packages/cli-common/api-report.md
@@ -3,6 +3,9 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
+// @public
+export const BACKSTAGE_JSON = 'backstage.json';
+
// @public
export function findPaths(searchDir: string): Paths;
diff --git a/packages/cli-common/package.json b/packages/cli-common/package.json
index 5916cdb017..cdb4b01b6d 100644
--- a/packages/cli-common/package.json
+++ b/packages/cli-common/package.json
@@ -1,7 +1,7 @@
{
"name": "@backstage/cli-common",
"description": "Common functionality used by cli, backend, and create-app",
- "version": "0.1.5",
+ "version": "0.1.6",
"private": false,
"main": "src/index.ts",
"types": "src/index.ts",
diff --git a/packages/cli-common/src/index.ts b/packages/cli-common/src/index.ts
index e7cda19514..8809764b43 100644
--- a/packages/cli-common/src/index.ts
+++ b/packages/cli-common/src/index.ts
@@ -20,6 +20,6 @@
* @packageDocumentation
*/
-export { findPaths } from './paths';
+export { findPaths, BACKSTAGE_JSON } from './paths';
export { isChildPath } from './isChildPath';
export type { Paths, ResolveFunc } from './paths';
diff --git a/packages/cli-common/src/paths.ts b/packages/cli-common/src/paths.ts
index c9f486752f..2b1ca34ca5 100644
--- a/packages/cli-common/src/paths.ts
+++ b/packages/cli-common/src/paths.ts
@@ -167,3 +167,10 @@ export function findPaths(searchDir: string): Paths {
resolveTargetRoot: (...paths) => resolvePath(getTargetRoot(), ...paths),
};
}
+
+/**
+ * The name of the backstage's config file
+ *
+ * @public
+ */
+export const BACKSTAGE_JSON = 'backstage.json';
diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md
index 0fc702851d..6be3a461bf 100644
--- a/packages/cli/CHANGELOG.md
+++ b/packages/cli/CHANGELOG.md
@@ -1,5 +1,65 @@
# @backstage/cli
+## 0.10.0
+
+### Minor Changes
+
+- ea99ef5198: Remove the `backend:build-image` command from the CLI and added more deprecation warnings to other deprecated fields like `--lax` and `remove-plugin`
+
+### Patch Changes
+
+- e7230ef814: Bump react-dev-utils to v12
+- 416b68675d: build(dependencies): bump `style-loader` from 1.2.1 to 3.3.1
+- Updated dependencies
+ - @backstage/config-loader@0.8.1
+
+## 0.9.1
+
+### Patch Changes
+
+- dde216acf4: Switch the default test coverage provider from the jest default one to `'v8'`, which provides much better coverage information when using the default Backstage test setup. This is considered a bug fix as the current coverage information is often very inaccurate.
+- 719cc87d2f: Disable ES transforms in tests transformed by the `jestSucraseTransform.js`. This is not considered a breaking change since all code is already transpiled this way in the development setup.
+- bab752e2b3: Change default port of backend from 7000 to 7007.
+
+ This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start.
+
+ You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values:
+
+ ```
+ backend:
+ listen: 0.0.0.0:7123
+ baseUrl: http://localhost:7123
+ ```
+
+ More information can be found here: https://backstage.io/docs/conf/writing
+
+- ee055cf6db: Update the default routes to use id instead of title
+- Updated dependencies
+ - @backstage/errors@0.1.5
+
+## 0.9.0
+
+### Minor Changes
+
+- 25f637f39f: Tweaked style insertion logic to make sure that JSS stylesheets always receive the highest priority.
+
+### Patch Changes
+
+- 677bfc2dd0: Keep backstage.json in sync
+
+ The `versions:bump` script now takes care about updating the `version` property inside `backstage.json` file. The file is created if is not present.
+
+- 8809b6c0dd: Update the json-schema dependency version.
+- fdfd2f8a62: remove double config dep
+- 1e99c73c75: Update internal usage of `configLoader.loadConfig` that now returns an object instead of an array of configs.
+- 6dcfe227a2: Added a scaffolder backend module template for the `create` command.
+- 4ca3542fdd: Fixed a bug where calling `backstage-cli backend:bundle --build-dependencies` with no dependencies to be built would cause all monorepo packages to be built instead.
+- 867ea81d15: bump `@rollup/plugin-commonjs` from 17.1.0 to 21.0.1
+- 16d06f6ac3: Introduces new `backstage-cli create` command to replace `create-plugin` and make space for creating a wider array of things. The create command also adds a new template for creating isomorphic common plugin packages.
+- Updated dependencies
+ - @backstage/config-loader@0.8.0
+ - @backstage/cli-common@0.1.6
+
## 0.8.2
### Patch Changes
diff --git a/packages/cli/config/jest.js b/packages/cli/config/jest.js
index e889d23c3a..1d33372c60 100644
--- a/packages/cli/config/jest.js
+++ b/packages/cli/config/jest.js
@@ -95,6 +95,7 @@ async function getProjectConfig(targetPath, displayName) {
...(displayName && { displayName }),
rootDir: path.resolve(targetPath, 'src'),
coverageDirectory: path.resolve(targetPath, 'coverage'),
+ coverageProvider: 'v8',
collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}', '!**/*.d.ts'],
moduleNameMapper: {
'\\.(css|less|scss|sss|styl)$': require.resolve('jest-css-modules'),
diff --git a/packages/cli/config/jestSucraseTransform.js b/packages/cli/config/jestSucraseTransform.js
index 01acfee38d..c88909d21f 100644
--- a/packages/cli/config/jestSucraseTransform.js
+++ b/packages/cli/config/jestSucraseTransform.js
@@ -46,7 +46,11 @@ function process(source, filePath) {
}
if (transforms) {
- return transform(source, { transforms, filePath }).code;
+ return transform(source, {
+ transforms,
+ filePath,
+ disableESTransforms: true,
+ }).code;
}
return source;
diff --git a/packages/cli/package.json b/packages/cli/package.json
index fcbc00f47d..fe8a529de1 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,7 +1,7 @@
{
"name": "@backstage/cli",
"description": "CLI for developing Backstage plugins and apps",
- "version": "0.8.2",
+ "version": "0.10.0",
"private": false,
"publishConfig": {
"access": "public"
@@ -28,19 +28,19 @@
"backstage-cli": "bin/backstage-cli"
},
"dependencies": {
- "@backstage/cli-common": "^0.1.5",
+ "@backstage/cli-common": "^0.1.6",
"@backstage/config": "^0.1.11",
- "@backstage/config-loader": "^0.7.2",
- "@backstage/errors": "^0.1.4",
+ "@backstage/config-loader": "^0.8.1",
+ "@backstage/errors": "^0.1.5",
"@backstage/types": "^0.1.1",
"@hot-loader/react-dom": "^16.13.0",
"@lerna/package-graph": "^4.0.0",
"@lerna/project": "^4.0.0",
"@octokit/request": "^5.4.12",
"@rollup/plugin-commonjs": "^21.0.1",
- "@rollup/plugin-json": "^4.0.2",
+ "@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.0",
- "@rollup/plugin-yaml": "^3.0.0",
+ "@rollup/plugin-yaml": "^3.1.0",
"@spotify/eslint-config-base": "^12.0.0",
"@spotify/eslint-config-react": "^12.0.0",
"@spotify/eslint-config-typescript": "^12.0.0",
@@ -62,7 +62,7 @@
"css-loader": "^5.2.6",
"dashify": "^2.0.0",
"diff": "^5.0.0",
- "esbuild": "^0.8.56",
+ "esbuild": "^0.14.1",
"eslint": "^7.30.0",
"eslint-config-prettier": "^8.3.0",
"eslint-formatter-friendly": "^7.0.0",
@@ -90,19 +90,19 @@
"postcss": "^8.1.0",
"process": "^0.11.10",
"react": "^16.0.0",
- "react-dev-utils": "^11.0.4",
+ "react-dev-utils": "^12.0.0-next.47",
"react-hot-loader": "^4.12.21",
"recursive-readdir": "^2.2.2",
"replace-in-file": "^6.0.0",
- "rollup": "2.44.x",
- "rollup-plugin-dts": "^3.0.1",
- "rollup-plugin-esbuild": "2.6.x",
+ "rollup": "^2.60.2",
+ "rollup-plugin-dts": "^4.0.1",
+ "rollup-plugin-esbuild": "^4.7.2",
"rollup-plugin-peer-deps-external": "^2.2.2",
"rollup-plugin-postcss": "^4.0.0",
"rollup-pluginutils": "^2.8.2",
"run-script-webpack-plugin": "^0.0.11",
"semver": "^7.3.2",
- "style-loader": "^1.2.1",
+ "style-loader": "^3.3.1",
"sucrase": "^3.20.2",
"tar": "^6.1.2",
"terser-webpack-plugin": "^5.1.3",
@@ -117,14 +117,14 @@
"yn": "^4.0.0"
},
"devDependencies": {
- "@backstage/backend-common": "^0.9.9",
+ "@backstage/backend-common": "^0.9.12",
"@backstage/config": "^0.1.11",
- "@backstage/core-components": "^0.7.3",
- "@backstage/core-plugin-api": "^0.1.13",
- "@backstage/core-app-api": "^0.1.20",
- "@backstage/dev-utils": "^0.2.12",
- "@backstage/test-utils": "^0.1.21",
- "@backstage/theme": "^0.2.13",
+ "@backstage/core-components": "^0.7.6",
+ "@backstage/core-plugin-api": "^0.2.2",
+ "@backstage/core-app-api": "^0.1.24",
+ "@backstage/dev-utils": "^0.2.13",
+ "@backstage/test-utils": "^0.1.23",
+ "@backstage/theme": "^0.2.14",
"@types/diff": "^5.0.0",
"@types/express": "^4.17.6",
"@types/fs-extra": "^9.0.1",
diff --git a/packages/cli/src/commands/app/build.ts b/packages/cli/src/commands/app/build.ts
index 22e7fb9de9..4fa3d1ac6e 100644
--- a/packages/cli/src/commands/app/build.ts
+++ b/packages/cli/src/commands/app/build.ts
@@ -20,8 +20,17 @@ import { buildBundle } from '../../lib/bundler';
import { parseParallel, PARALLEL_ENV_VAR } from '../../lib/parallel';
import { loadCliConfig } from '../../lib/config';
import { paths } from '../../lib/paths';
+import chalk from 'chalk';
export default async (cmd: Command) => {
+ if (cmd.lax) {
+ console.warn(
+ chalk.yellow(
+ `[DEPRECATED] - The --lax option is deprecated and will be removed in the future. Please open an issue towards https://github.com/backstage/backstage that describes your use-case if you need the flag to stay around.`,
+ ),
+ );
+ }
+
const { name } = await fs.readJson(paths.resolveTarget('package.json'));
await buildBundle({
entry: 'src/index',
diff --git a/packages/cli/src/commands/backend/build.ts b/packages/cli/src/commands/backend/build.ts
index 6cb1347b1e..8c25f56746 100644
--- a/packages/cli/src/commands/backend/build.ts
+++ b/packages/cli/src/commands/backend/build.ts
@@ -14,10 +14,12 @@
* limitations under the License.
*/
+import { Command } from 'commander';
import { buildPackage, Output } from '../../lib/builder';
-export default async () => {
+export default async (cmd: Command) => {
await buildPackage({
outputs: new Set([Output.cjs, Output.types]),
+ minify: cmd.minify,
});
};
diff --git a/packages/cli/src/commands/backend/buildImage.ts b/packages/cli/src/commands/backend/buildImage.ts
deleted file mode 100644
index b352e38203..0000000000
--- a/packages/cli/src/commands/backend/buildImage.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2020 The Backstage Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { Command } from 'commander';
-import { yellow } from 'chalk';
-import fs from 'fs-extra';
-import { join as joinPath, relative as relativePath } from 'path';
-import { createDistWorkspace } from '../../lib/packager';
-import { paths } from '../../lib/paths';
-import { run } from '../../lib/run';
-import { parseParallel, PARALLEL_ENV_VAR } from '../../lib/parallel';
-
-const PKG_PATH = 'package.json';
-
-export default async (cmd: Command) => {
- // Skip the preparation steps if we're being asked for help
- if (cmd.args.includes('--help')) {
- await run('docker', ['image', 'build', '--help']);
- return;
- }
-
- console.warn(
- yellow(`
-The backend:build-image command is deprecated and will be removed in the future.
-Please use the backend:bundle command instead along with your own Docker setup.
-
- https://backstage.io/docs/deployment/docker
-`),
- );
-
- const pkgPath = paths.resolveTarget(PKG_PATH);
- const pkg = await fs.readJson(pkgPath);
- const appConfigs = await findAppConfigs();
- const npmrc = (await fs.pathExists(paths.resolveTargetRoot('.npmrc')))
- ? ['.npmrc']
- : [];
- const tempDistWorkspace = await createDistWorkspace([pkg.name], {
- buildDependencies: Boolean(cmd.build),
- files: [
- 'package.json',
- 'yarn.lock',
- ...npmrc,
- ...appConfigs,
- { src: paths.resolveTarget('Dockerfile'), dest: 'Dockerfile' },
- ],
- parallel: parseParallel(process.env[PARALLEL_ENV_VAR]),
- skeleton: 'skeleton.tar',
- });
- console.log(`Dist workspace ready at ${tempDistWorkspace}`);
-
- // all args are forwarded to docker build
- await run('docker', ['image', 'build', '.', ...cmd.args], {
- cwd: tempDistWorkspace,
- });
-
- await fs.remove(tempDistWorkspace);
-};
-
-/**
- * Find all config files to copy into the image
- */
-async function findAppConfigs(): Promise {
- const files = [];
-
- for (const name of await fs.readdir(paths.targetRoot)) {
- if (name.startsWith('app-config.') && name.endsWith('.yaml')) {
- files.push(name);
- }
- }
-
- if (paths.targetRoot !== paths.targetDir) {
- const dirPath = relativePath(paths.targetRoot, paths.targetDir);
-
- for (const name of await fs.readdir(paths.targetDir)) {
- if (name.startsWith('app-config.') && name.endsWith('.yaml')) {
- files.push(joinPath(dirPath, name));
- }
- }
- }
-
- return files;
-}
diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts
index a37d5e9ffe..94a8104ff1 100644
--- a/packages/cli/src/commands/index.ts
+++ b/packages/cli/src/commands/index.ts
@@ -30,7 +30,10 @@ export function registerCommands(program: CommanderStatic) {
.command('app:build')
.description('Build an app for a production release')
.option('--stats', 'Write bundle stats to output directory')
- .option('--lax', 'Do not require environment variables to be set')
+ .option(
+ '--lax',
+ '[DEPRECATED] - Do not require environment variables to be set',
+ )
.option(...configOption)
.action(lazy(() => import('./app/build').then(m => m.default)));
@@ -44,6 +47,7 @@ export function registerCommands(program: CommanderStatic) {
program
.command('backend:build')
.description('Build a backend plugin')
+ .option('--minify', 'Minify the generated code')
.action(lazy(() => import('./backend/build').then(m => m.default)));
program
@@ -55,16 +59,6 @@ export function registerCommands(program: CommanderStatic) {
)
.action(lazy(() => import('./backend/bundle').then(m => m.default)));
- program
- .command('backend:build-image')
- .allowUnknownOption(true)
- .helpOption(', --backstage-cli-help') // Let docker handle --help
- .option('--build', 'Build packages before packing them into the image')
- .description(
- 'Bundles the package into a docker image. This command is deprecated and will be removed.',
- )
- .action(lazy(() => import('./backend/buildImage').then(m => m.default)));
-
program
.command('backend:dev')
.description('Start local development server with HMR for the backend')
@@ -115,7 +109,7 @@ export function registerCommands(program: CommanderStatic) {
program
.command('remove-plugin')
- .description('Removes plugin in the current repository')
+ .description('[DEPRECATED] - Removes plugin in the current repository')
.action(
lazy(() => import('./remove-plugin/removePlugin').then(m => m.default)),
);
@@ -123,6 +117,7 @@ export function registerCommands(program: CommanderStatic) {
program
.command('plugin:build')
.description('Build a plugin')
+ .option('--minify', 'Minify the generated code')
.action(lazy(() => import('./plugin/build').then(m => m.default)));
program
diff --git a/packages/cli/src/commands/plugin/build.ts b/packages/cli/src/commands/plugin/build.ts
index 8a63a017a2..9c5e173989 100644
--- a/packages/cli/src/commands/plugin/build.ts
+++ b/packages/cli/src/commands/plugin/build.ts
@@ -14,10 +14,12 @@
* limitations under the License.
*/
+import { Command } from 'commander';
import { buildPackage, Output } from '../../lib/builder';
-export default async () => {
+export default async (cmd: Command) => {
await buildPackage({
outputs: new Set([Output.esm, Output.types]),
+ minify: cmd.minify,
});
};
diff --git a/packages/cli/src/commands/remove-plugin/removePlugin.ts b/packages/cli/src/commands/remove-plugin/removePlugin.ts
index 11fdc8bb11..a82bfda36d 100644
--- a/packages/cli/src/commands/remove-plugin/removePlugin.ts
+++ b/packages/cli/src/commands/remove-plugin/removePlugin.ts
@@ -184,6 +184,12 @@ export const removeReferencesFromAppPackage = async (
};
export default async () => {
+ console.warn(
+ chalk.yellow(
+ '[DEPRECATED] - The remove-plugin command is deprecated and will be removed in the future.',
+ ),
+ );
+
const questions: Question[] = [
{
type: 'input',
diff --git a/packages/cli/src/commands/versions/bump.test.ts b/packages/cli/src/commands/versions/bump.test.ts
index 96bd1ab6fd..bc4049f1f4 100644
--- a/packages/cli/src/commands/versions/bump.test.ts
+++ b/packages/cli/src/commands/versions/bump.test.ts
@@ -20,7 +20,7 @@ import { resolve as resolvePath } from 'path';
import { paths } from '../../lib/paths';
import { mapDependencies } from '../../lib/versioning';
import * as runObj from '../../lib/run';
-import bump from './bump';
+import bump, { bumpBackstageJsonVersion } from './bump';
import { withLogCollector } from '@backstage/test-utils';
// Remove log coloring to simplify log matching
@@ -141,7 +141,7 @@ describe('bump', () => {
'Version bump complete!',
]);
- expect(runObj.runPlain).toHaveBeenCalledTimes(3);
+ expect(runObj.runPlain).toHaveBeenCalledTimes(4);
expect(runObj.runPlain).toHaveBeenCalledWith(
'yarn',
'info',
@@ -245,3 +245,64 @@ describe('bump', () => {
});
});
});
+
+describe('bumpBackstageJsonVersion', () => {
+ afterEach(() => {
+ mockFs.restore();
+ jest.resetAllMocks();
+ });
+
+ it('should bump version in backstage.json', async () => {
+ mockFs({
+ '/backstage.json': JSON.stringify({ version: '0.0.1' }),
+ });
+ paths.targetDir = '/';
+ const latest = '1.4.1';
+ jest
+ .spyOn(paths, 'resolveTargetRoot')
+ .mockImplementation((...path) => resolvePath('/', ...path));
+ jest.spyOn(runObj, 'runPlain').mockImplementation(async (...[, , , name]) =>
+ JSON.stringify({
+ type: 'inspect',
+ data: {
+ name,
+ 'dist-tags': {
+ latest,
+ },
+ },
+ }),
+ );
+ jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
+
+ await bumpBackstageJsonVersion();
+
+ const json = await fs.readJson('/backstage.json');
+ expect(json).toEqual({ version: '1.4.1' });
+ });
+
+ it("should create backstage.json if doesn't exist", async () => {
+ mockFs({});
+ paths.targetDir = '/';
+ const latest = '1.4.1';
+ jest
+ .spyOn(paths, 'resolveTargetRoot')
+ .mockImplementation((...path) => resolvePath('/', ...path));
+ jest.spyOn(runObj, 'runPlain').mockImplementation(async (...[, , , name]) =>
+ JSON.stringify({
+ type: 'inspect',
+ data: {
+ name,
+ 'dist-tags': {
+ latest,
+ },
+ },
+ }),
+ );
+ jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
+
+ await bumpBackstageJsonVersion();
+
+ const json = await fs.readJson('/backstage.json');
+ expect(json).toEqual({ version: '1.4.1' });
+ });
+});
diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts
index 7b967839b2..3898c1654a 100644
--- a/packages/cli/src/commands/versions/bump.ts
+++ b/packages/cli/src/commands/versions/bump.ts
@@ -27,6 +27,7 @@ import {
Lockfile,
} from '../../lib/versioning';
import { includedFilter, forbiddenDuplicatesFilter } from './lint';
+import { BACKSTAGE_JSON } from '@backstage/cli-common';
const DEP_TYPES = [
'dependencies',
@@ -182,6 +183,10 @@ export default async () => {
await fs.writeJson(pkgPath, pkgJson, { spaces: 2 });
});
+ console.log();
+
+ await bumpBackstageJsonVersion();
+
console.log();
console.log(
`Running ${chalk.blue('yarn install')} to install new versions`,
@@ -271,6 +276,41 @@ function createVersionFinder() {
};
}
+export async function bumpBackstageJsonVersion() {
+ const backstageJsonPath = paths.resolveTargetRoot(BACKSTAGE_JSON);
+ const backstageJson = await fs.readJSON(backstageJsonPath).catch(e => {
+ if (e.code === 'ENOENT') {
+ // gracefully continue in case the file doesn't exist
+ return;
+ }
+ throw e;
+ });
+
+ const info = await fetchPackageInfo('@backstage/create-app');
+ const { latest } = info['dist-tags'];
+
+ if (backstageJson?.version === latest) {
+ return;
+ }
+
+ console.log(
+ chalk.yellow(
+ typeof backstageJson === 'undefined'
+ ? `Creating ${BACKSTAGE_JSON}`
+ : `Bumping version in ${BACKSTAGE_JSON}`,
+ ),
+ );
+
+ await fs.writeJson(
+ backstageJsonPath,
+ { ...backstageJson, version: latest },
+ {
+ spaces: 2,
+ encoding: 'utf8',
+ },
+ );
+}
+
async function workerThreads(
count: number,
items: IterableIterator,
diff --git a/packages/cli/src/lib/builder/config.ts b/packages/cli/src/lib/builder/config.ts
index 3c95a87bdd..ba60e8fea9 100644
--- a/packages/cli/src/lib/builder/config.ts
+++ b/packages/cli/src/lib/builder/config.ts
@@ -100,6 +100,7 @@ export const makeConfigs = async (
}),
esbuild({
target: 'es2019',
+ minify: options.minify,
}),
],
});
diff --git a/packages/cli/src/lib/builder/types.ts b/packages/cli/src/lib/builder/types.ts
index f75d1b1dda..e88c388013 100644
--- a/packages/cli/src/lib/builder/types.ts
+++ b/packages/cli/src/lib/builder/types.ts
@@ -22,4 +22,5 @@ export enum Output {
export type BuildOptions = {
outputs: Set
+ {
result={document}
/>
);
+ case 'techdocs':
+ return (
+
+ );
default:
return (
{
const logger = root.child({ type: 'plugin', plugin });
const database = databaseManager.forPlugin(plugin);
const cache = cacheManager.forPlugin(plugin);
- return { logger, database, cache, config, reader, discovery };
+ return { logger, database, cache, config, reader, discovery, tokenManager };
};
}
diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts
index 7fc317d23d..f23b0c7bcf 100644
--- a/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts
+++ b/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts
@@ -6,21 +6,36 @@ import {
} from '@backstage/plugin-search-backend-node';
import { PluginEnvironment } from '../types';
import { DefaultCatalogCollator } from '@backstage/plugin-catalog-backend';
+import { DefaultTechDocsCollator } from '@backstage/plugin-techdocs-backend';
export default async function createPlugin({
logger,
discovery,
config,
+ tokenManager,
}: PluginEnvironment) {
// Initialize a connection to a search engine.
const searchEngine = new LunrSearchEngine({ logger });
const indexBuilder = new IndexBuilder({ logger, searchEngine });
// Collators are responsible for gathering documents known to plugins. This
- // particular collator gathers entities from the software catalog.
+ // collator gathers entities from the software catalog.
indexBuilder.addCollator({
defaultRefreshIntervalSeconds: 600,
- collator: DefaultCatalogCollator.fromConfig(config, { discovery }),
+ collator: DefaultCatalogCollator.fromConfig(config, {
+ discovery,
+ tokenManager,
+ }),
+ });
+
+ // collator gathers entities from techdocs.
+ indexBuilder.addCollator({
+ defaultRefreshIntervalSeconds: 600,
+ collator: DefaultTechDocsCollator.fromConfig(config, {
+ discovery,
+ logger,
+ tokenManager,
+ }),
});
// The scheduler controls when documents are gathered from collators and sent
diff --git a/packages/create-app/templates/default-app/packages/backend/src/types.ts b/packages/create-app/templates/default-app/packages/backend/src/types.ts
index 6c78a2a90c..b1e2e0a1df 100644
--- a/packages/create-app/templates/default-app/packages/backend/src/types.ts
+++ b/packages/create-app/templates/default-app/packages/backend/src/types.ts
@@ -4,6 +4,7 @@ import {
PluginCacheManager,
PluginDatabaseManager,
PluginEndpointDiscovery,
+ TokenManager,
UrlReader,
} from '@backstage/backend-common';
@@ -14,4 +15,5 @@ export type PluginEnvironment = {
config: Config;
reader: UrlReader;
discovery: PluginEndpointDiscovery;
+ tokenManager: TokenManager;
};
diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md
index 6afb45d79b..b882155a3d 100644
--- a/packages/dev-utils/CHANGELOG.md
+++ b/packages/dev-utils/CHANGELOG.md
@@ -1,5 +1,21 @@
# @backstage/dev-utils
+## 0.2.13
+
+### Patch Changes
+
+- 58a4e67ded: Add theme switcher to sidebar of dev app.
+- 014cbf8cb9: Migrated to using `@backstage/app-defaults`.
+- Updated dependencies
+ - @backstage/catalog-model@0.9.7
+ - @backstage/plugin-catalog-react@0.6.4
+ - @backstage/core-components@0.7.4
+ - @backstage/core-plugin-api@0.2.0
+ - @backstage/core-app-api@0.1.21
+ - @backstage/test-utils@0.1.22
+ - @backstage/app-defaults@0.1.1
+ - @backstage/integration-react@0.1.14
+
## 0.2.12
### Patch Changes
diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json
index 974329e8d4..110d774777 100644
--- a/packages/dev-utils/package.json
+++ b/packages/dev-utils/package.json
@@ -1,7 +1,7 @@
{
"name": "@backstage/dev-utils",
"description": "Utilities for developing Backstage plugins.",
- "version": "0.2.12",
+ "version": "0.2.13",
"private": false,
"publishConfig": {
"access": "public",
@@ -29,15 +29,15 @@
"clean": "backstage-cli clean"
},
"dependencies": {
- "@backstage/app-defaults": "^0.1.0",
- "@backstage/core-app-api": "^0.1.18",
- "@backstage/core-components": "^0.7.1",
- "@backstage/core-plugin-api": "^0.1.11",
- "@backstage/catalog-model": "^0.9.5",
- "@backstage/integration-react": "^0.1.12",
- "@backstage/plugin-catalog-react": "^0.6.1",
- "@backstage/test-utils": "^0.1.19",
- "@backstage/theme": "^0.2.11",
+ "@backstage/app-defaults": "^0.1.1",
+ "@backstage/core-app-api": "^0.1.24",
+ "@backstage/core-components": "^0.7.6",
+ "@backstage/core-plugin-api": "^0.2.2",
+ "@backstage/catalog-model": "^0.9.7",
+ "@backstage/integration-react": "^0.1.14",
+ "@backstage/plugin-catalog-react": "^0.6.4",
+ "@backstage/test-utils": "^0.1.22",
+ "@backstage/theme": "^0.2.14",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@testing-library/jest-dom": "^5.10.1",
@@ -53,7 +53,7 @@
"zen-observable": "^0.8.15"
},
"devDependencies": {
- "@backstage/cli": "^0.8.0",
+ "@backstage/cli": "^0.10.0",
"@types/jest": "^26.0.7",
"@types/node": "^14.14.32"
},
diff --git a/packages/dev-utils/src/devApp/SidebarThemeSwitcher.test.tsx b/packages/dev-utils/src/devApp/SidebarThemeSwitcher.test.tsx
index 3907dc1007..43eaa6218c 100644
--- a/packages/dev-utils/src/devApp/SidebarThemeSwitcher.test.tsx
+++ b/packages/dev-utils/src/devApp/SidebarThemeSwitcher.test.tsx
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
+
import { AppThemeApi, appThemeApiRef } from '@backstage/core-plugin-api';
-import { renderInTestApp } from '@backstage/test-utils';
+import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
import { BackstageTheme } from '@backstage/theme';
import userEvent from '@testing-library/user-event';
import React from 'react';
@@ -24,7 +24,6 @@ import { SidebarThemeSwitcher } from './SidebarThemeSwitcher';
describe('SidebarThemeSwitcher', () => {
let appThemeApi: jest.Mocked;
- let apiRegistry: ApiRegistry;
beforeEach(() => {
appThemeApi = {
@@ -51,15 +50,13 @@ describe('SidebarThemeSwitcher', () => {
theme: {} as unknown as BackstageTheme,
},
]);
-
- apiRegistry = ApiRegistry.with(appThemeApiRef, appThemeApi);
});
it('should display current theme', async () => {
const { getByLabelText, getByRole, getByText } = await renderInTestApp(
-
+
- ,
+ ,
);
const button = getByLabelText('Switch Theme');
@@ -76,9 +73,9 @@ describe('SidebarThemeSwitcher', () => {
it('should select different theme', async () => {
const { getByLabelText, getByRole, getByText } = await renderInTestApp(
-
+
- ,
+ ,
);
const button = getByLabelText('Switch Theme');
diff --git a/packages/e2e-test/src/commands/run.ts b/packages/e2e-test/src/commands/run.ts
index e1d8473121..a7456f91dd 100644
--- a/packages/e2e-test/src/commands/run.ts
+++ b/packages/e2e-test/src/commands/run.ts
@@ -471,7 +471,7 @@ async function testBackendStart(appDir: string, isPostgres: boolean) {
print('Try to fetch entities from the backend');
// Try fetch entities, should be ok
- await fetch('http://localhost:7000/api/catalog/entities').then(res =>
+ await fetch('http://localhost:7007/api/catalog/entities').then(res =>
res.json(),
);
print('Entities fetched successfully');
diff --git a/packages/embedded-techdocs-app/CHANGELOG.md b/packages/embedded-techdocs-app/CHANGELOG.md
new file mode 100644
index 0000000000..13aa9aabe3
--- /dev/null
+++ b/packages/embedded-techdocs-app/CHANGELOG.md
@@ -0,0 +1,29 @@
+# embedded-techdocs-app
+
+## 0.2.55
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/core-components@0.7.6
+ - @backstage/theme@0.2.14
+ - @backstage/cli@0.10.0
+ - @backstage/core-plugin-api@0.2.2
+ - @backstage/core-app-api@0.1.24
+ - @backstage/plugin-techdocs@0.12.8
+
+## 0.2.53
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/plugin-catalog@0.7.3
+ - @backstage/cli@0.9.0
+ - @backstage/catalog-model@0.9.7
+ - @backstage/plugin-techdocs@0.12.6
+ - @backstage/core-components@0.7.4
+ - @backstage/core-plugin-api@0.2.0
+ - @backstage/core-app-api@0.1.21
+ - @backstage/test-utils@0.1.22
+ - @backstage/app-defaults@0.1.1
+ - @backstage/integration-react@0.1.14
diff --git a/packages/embedded-techdocs-app/app-config.dev.yaml b/packages/embedded-techdocs-app/app-config.dev.yaml
index 2d1bad2808..02d68c940e 100644
--- a/packages/embedded-techdocs-app/app-config.dev.yaml
+++ b/packages/embedded-techdocs-app/app-config.dev.yaml
@@ -5,8 +5,8 @@ app:
baseUrl: http://localhost:3000
backend:
- baseUrl: http://localhost:7000
+ baseUrl: http://localhost:7007
techdocs:
builder: 'external'
- requestUrl: http://localhost:7000/api
+ requestUrl: http://localhost:7007/api
diff --git a/packages/embedded-techdocs-app/package.json b/packages/embedded-techdocs-app/package.json
index 7901d46fee..24db320eaa 100644
--- a/packages/embedded-techdocs-app/package.json
+++ b/packages/embedded-techdocs-app/package.json
@@ -1,21 +1,21 @@
{
"name": "embedded-techdocs-app",
- "version": "0.0.0",
+ "version": "0.2.55",
"private": true,
"bundled": true,
"dependencies": {
- "@backstage/app-defaults": "^0.1.0",
- "@backstage/catalog-model": "^0.9.5",
- "@backstage/cli": "^0.8.0",
+ "@backstage/app-defaults": "^0.1.1",
+ "@backstage/catalog-model": "^0.9.7",
+ "@backstage/cli": "^0.10.0",
"@backstage/config": "^0.1.10",
- "@backstage/core-app-api": "^0.1.18",
- "@backstage/core-components": "^0.7.1",
- "@backstage/core-plugin-api": "^0.1.11",
- "@backstage/integration-react": "^0.1.12",
- "@backstage/plugin-catalog": "^0.7.2",
- "@backstage/plugin-techdocs": "^0.12.3",
- "@backstage/test-utils": "^0.1.19",
- "@backstage/theme": "^0.2.11",
+ "@backstage/core-app-api": "^0.1.24",
+ "@backstage/core-components": "^0.7.6",
+ "@backstage/core-plugin-api": "^0.2.2",
+ "@backstage/integration-react": "^0.1.14",
+ "@backstage/plugin-catalog": "^0.7.3",
+ "@backstage/plugin-techdocs": "^0.12.8",
+ "@backstage/test-utils": "^0.1.22",
+ "@backstage/theme": "^0.2.14",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"history": "^5.0.0",
@@ -26,7 +26,7 @@
"react-use": "^17.2.4"
},
"devDependencies": {
- "@backstage/cli": "^089.0",
+ "@backstage/cli": "^0.10.0",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^13.1.8",
diff --git a/packages/embedded-techdocs-app/src/App.test.tsx b/packages/embedded-techdocs-app/src/App.test.tsx
index a5a4374976..f177433d95 100644
--- a/packages/embedded-techdocs-app/src/App.test.tsx
+++ b/packages/embedded-techdocs-app/src/App.test.tsx
@@ -26,9 +26,9 @@ describe('App', () => {
{
data: {
app: { title: 'Test' },
- backend: { baseUrl: 'http://localhost:7000' },
+ backend: { baseUrl: 'http://localhost:7007' },
techdocs: {
- storageUrl: 'http://localhost:7000/api/techdocs/static/docs',
+ storageUrl: 'http://localhost:7007/api/techdocs/static/docs',
},
},
context: 'test',
diff --git a/packages/errors/CHANGELOG.md b/packages/errors/CHANGELOG.md
index 9ea2f47cbb..02b28cdf1d 100644
--- a/packages/errors/CHANGELOG.md
+++ b/packages/errors/CHANGELOG.md
@@ -1,5 +1,12 @@
# @backstage/errors
+## 0.1.5
+
+### Patch Changes
+
+- 4d09c60256: Deprecate `parseErrorResponse` in favour of `parseErrorResponseBody`. Deprecate `data` field inside `ErrorResponse` in favour of `body`.
+ Rename the error name for unknown errors from `unknown` to `error`.
+
## 0.1.4
### Patch Changes
diff --git a/packages/errors/api-report.md b/packages/errors/api-report.md
index 4984919e6a..97b011bad6 100644
--- a/packages/errors/api-report.md
+++ b/packages/errors/api-report.md
@@ -33,8 +33,11 @@ export type ErrorLike = {
[unknownKeys: string]: unknown;
};
+// @public @deprecated
+export type ErrorResponse = ErrorResponseBody;
+
// @public
-export type ErrorResponse = {
+export type ErrorResponseBody = {
error: SerializedError;
request?: {
method: string;
@@ -65,19 +68,27 @@ export class NotFoundError extends CustomErrorBase {}
// @public
export class NotModifiedError extends CustomErrorBase {}
-// @public
+// @public @deprecated
export function parseErrorResponse(response: Response): Promise;
+// @public
+export function parseErrorResponseBody(
+ response: Response,
+): Promise;
+
// @public
export class ResponseError extends Error {
+ // @deprecated
constructor(props: {
message: string;
response: Response;
- data: ErrorResponse;
+ data: ErrorResponseBody;
cause: Error;
});
+ readonly body: ErrorResponseBody;
readonly cause: Error;
- readonly data: ErrorResponse;
+ // @deprecated
+ get data(): ErrorResponseBody;
static fromResponse(response: Response): Promise;
readonly response: Response;
}
diff --git a/packages/errors/package.json b/packages/errors/package.json
index fe62bb3bba..d0d59ffd6a 100644
--- a/packages/errors/package.json
+++ b/packages/errors/package.json
@@ -1,7 +1,7 @@
{
"name": "@backstage/errors",
"description": "Common utilities for error handling within Backstage",
- "version": "0.1.4",
+ "version": "0.1.5",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -35,7 +35,7 @@
"serialize-error": "^8.0.1"
},
"devDependencies": {
- "@backstage/cli": "^0.8.1",
+ "@backstage/cli": "^0.10.0",
"@types/jest": "^26.0.7"
},
"files": [
diff --git a/packages/errors/src/errors/ResponseError.ts b/packages/errors/src/errors/ResponseError.ts
index f74ad2fa0c..a20035cd8c 100644
--- a/packages/errors/src/errors/ResponseError.ts
+++ b/packages/errors/src/errors/ResponseError.ts
@@ -14,16 +14,16 @@
* limitations under the License.
*/
+import { deserializeError } from '../serialization';
import {
- parseErrorResponse,
- ErrorResponse,
- deserializeError,
-} from '../serialization';
+ ErrorResponseBody,
+ parseErrorResponseBody,
+} from '../serialization/response';
/**
* An error thrown as the result of a failed server request.
*
- * The server is expected to respond on the ErrorResponse format.
+ * The server is expected to respond on the ErrorResponseBody format.
*
* @public
*/
@@ -39,7 +39,7 @@ export class ResponseError extends Error {
/**
* The parsed JSON error body, as sent by the server.
*/
- readonly data: ErrorResponse;
+ readonly body: ErrorResponseBody;
/**
* The Error cause, as seen by the remote server. This is parsed out of the
@@ -60,7 +60,7 @@ export class ResponseError extends Error {
* been consumed before.
*/
static async fromResponse(response: Response): Promise {
- const data = await parseErrorResponse(response);
+ const data = await parseErrorResponseBody(response);
const status = data.response.statusCode || response.status;
const statusText = data.error.name || response.statusText;
@@ -75,16 +75,28 @@ export class ResponseError extends Error {
});
}
+ /**
+ * @deprecated will be removed.
+ **/
constructor(props: {
message: string;
response: Response;
- data: ErrorResponse;
+ data: ErrorResponseBody;
cause: Error;
}) {
super(props.message);
this.name = 'ResponseError';
this.response = props.response;
- this.data = props.data;
+ this.body = props.data;
this.cause = props.cause;
}
+ /**
+ * The parsed JSON error body, as sent by the server.
+ * @deprecated use body instead.
+ */
+ get data(): ErrorResponseBody {
+ // eslint-disable-next-line no-console
+ console.warn('ErrorResponse.data is deprecated, use .body instead.');
+ return this.body;
+ }
}
diff --git a/packages/errors/src/serialization/index.ts b/packages/errors/src/serialization/index.ts
index d37b04297a..b86661d496 100644
--- a/packages/errors/src/serialization/index.ts
+++ b/packages/errors/src/serialization/index.ts
@@ -16,5 +16,5 @@
export { deserializeError, serializeError, stringifyError } from './error';
export type { SerializedError } from './error';
-export { parseErrorResponse } from './response';
-export type { ErrorResponse } from './response';
+export { parseErrorResponse, parseErrorResponseBody } from './response';
+export type { ErrorResponse, ErrorResponseBody } from './response';
diff --git a/packages/errors/src/serialization/response.test.ts b/packages/errors/src/serialization/response.test.ts
index 54a3641657..fe1cc1e9c4 100644
--- a/packages/errors/src/serialization/response.test.ts
+++ b/packages/errors/src/serialization/response.test.ts
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-import { parseErrorResponse, ErrorResponse } from './response';
+import { parseErrorResponseBody, ErrorResponseBody } from './response';
-describe('parseErrorResponse', () => {
+describe('parseErrorResponseBody', () => {
it('handles the happy path', async () => {
- const body: ErrorResponse = {
+ const body: ErrorResponseBody = {
error: { name: 'Fours', message: 'Expected fives', stack: 'lines' },
request: { method: 'GET', url: '/' },
response: { statusCode: 444 },
@@ -31,13 +31,13 @@ describe('parseErrorResponse', () => {
headers: new Headers({ 'Content-Type': 'application/json' }),
};
- await expect(parseErrorResponse(response as Response)).resolves.toEqual(
+ await expect(parseErrorResponseBody(response as Response)).resolves.toEqual(
body,
);
});
it('uses request header and text body when wrong content type, even if parsable', async () => {
- const body: ErrorResponse = {
+ const body: ErrorResponseBody = {
error: { name: 'Threes', message: 'Expected twos' },
request: { method: 'GET', url: '/' },
response: { statusCode: 333 },
@@ -50,19 +50,21 @@ describe('parseErrorResponse', () => {
headers: new Headers({ 'Content-Type': 'not-application/not-json' }),
};
- await expect(parseErrorResponse(response as Response)).resolves.toEqual({
- error: {
- name: 'Unknown',
- message: `Request failed with status 444 Fours, ${JSON.stringify(
- body,
- )}`,
+ await expect(parseErrorResponseBody(response as Response)).resolves.toEqual(
+ {
+ error: {
+ name: 'Error',
+ message: `Request failed with status 444 Fours, ${JSON.stringify(
+ body,
+ )}`,
+ },
+ response: { statusCode: 444 },
},
- response: { statusCode: 444 },
- });
+ );
});
it('uses request header and text body when not parsable', async () => {
- const body: ErrorResponse = {
+ const body: ErrorResponseBody = {
error: { name: 'Threes', message: 'Expected twos' },
request: { method: 'GET', url: '/' },
response: { statusCode: 333 },
@@ -75,15 +77,17 @@ describe('parseErrorResponse', () => {
headers: new Headers({ 'Content-Type': 'application/json' }),
};
- await expect(parseErrorResponse(response as Response)).resolves.toEqual({
- error: {
- name: 'Unknown',
- message: `Request failed with status 444 Fours, ${JSON.stringify(
- body,
- ).substring(1)}`,
+ await expect(parseErrorResponseBody(response as Response)).resolves.toEqual(
+ {
+ error: {
+ name: 'Error',
+ message: `Request failed with status 444 Fours, ${JSON.stringify(
+ body,
+ ).substring(1)}`,
+ },
+ response: { statusCode: 444 },
},
- response: { statusCode: 444 },
- });
+ );
});
it('uses request header when failing to get body', async () => {
@@ -96,12 +100,14 @@ describe('parseErrorResponse', () => {
headers: new Headers({ 'Content-Type': 'application/json' }),
};
- await expect(parseErrorResponse(response as Response)).resolves.toEqual({
- error: {
- name: 'Unknown',
- message: `Request failed with status 444 Fours`,
+ await expect(parseErrorResponseBody(response as Response)).resolves.toEqual(
+ {
+ error: {
+ name: 'Error',
+ message: `Request failed with status 444 Fours`,
+ },
+ response: { statusCode: 444 },
},
- response: { statusCode: 444 },
- });
+ );
});
});
diff --git a/packages/errors/src/serialization/response.ts b/packages/errors/src/serialization/response.ts
index 50bf8f7ab1..4a1731646d 100644
--- a/packages/errors/src/serialization/response.ts
+++ b/packages/errors/src/serialization/response.ts
@@ -20,8 +20,16 @@ import { SerializedError } from './error';
* A standard shape of JSON data returned as the body of backend errors.
*
* @public
+ * @deprecated - Use {@link ErrorResponseBody} instead.
*/
-export type ErrorResponse = {
+export type ErrorResponse = ErrorResponseBody;
+
+/**
+ * A standard shape of JSON data returned as the body of backend errors.
+ *
+ * @public
+ */
+export type ErrorResponseBody = {
/** Details of the error that was caught */
error: SerializedError;
@@ -51,10 +59,29 @@ export type ErrorResponse = {
*
* @public
* @param response - The response of a failed request
+ * @deprecated - Use {@link parseErrorResponseBody} instead.
*/
export async function parseErrorResponse(
response: Response,
): Promise {
+ return parseErrorResponseBody(response);
+}
+
+/**
+ * Attempts to construct an ErrorResponseBody out of a failed server request.
+ * Assumes that the response has already been checked to be not ok. This
+ * function consumes the body of the response, and assumes that it hasn't
+ * been consumed before.
+ *
+ * The code is forgiving, and constructs a useful synthetic body as best it can
+ * if the response body wasn't on the expected form.
+ *
+ * @public
+ * @param response - The response of a failed request
+ */
+export async function parseErrorResponseBody(
+ response: Response,
+): Promise {
try {
const text = await response.text();
if (text) {
@@ -73,7 +100,7 @@ export async function parseErrorResponse(
return {
error: {
- name: 'Unknown',
+ name: 'Error',
message: `Request failed with status ${response.status} ${response.statusText}, ${text}`,
},
response: {
@@ -87,7 +114,7 @@ export async function parseErrorResponse(
return {
error: {
- name: 'Unknown',
+ name: 'Error',
message: `Request failed with status ${response.status} ${response.statusText}`,
},
response: {
diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md
index a2a0d0c368..e972b4d4e8 100644
--- a/packages/integration-react/CHANGELOG.md
+++ b/packages/integration-react/CHANGELOG.md
@@ -1,5 +1,13 @@
# @backstage/integration-react
+## 0.1.14
+
+### Patch Changes
+
+- Updated dependencies
+ - @backstage/core-components@0.7.4
+ - @backstage/core-plugin-api@0.2.0
+
## 0.1.13
### Patch Changes
diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json
index b703203ab2..be38d92b64 100644
--- a/packages/integration-react/package.json
+++ b/packages/integration-react/package.json
@@ -1,7 +1,7 @@
{
"name": "@backstage/integration-react",
"description": "Frontend package for managing integrations towards external systems",
- "version": "0.1.13",
+ "version": "0.1.14",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -22,10 +22,10 @@
},
"dependencies": {
"@backstage/config": "^0.1.11",
- "@backstage/core-components": "^0.7.2",
- "@backstage/core-plugin-api": "^0.1.12",
- "@backstage/integration": "^0.6.9",
- "@backstage/theme": "^0.2.12",
+ "@backstage/core-components": "^0.7.6",
+ "@backstage/core-plugin-api": "^0.2.2",
+ "@backstage/integration": "^0.6.10",
+ "@backstage/theme": "^0.2.14",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
@@ -34,9 +34,9 @@
"react-use": "^17.2.4"
},
"devDependencies": {
- "@backstage/cli": "^0.8.1",
- "@backstage/dev-utils": "^0.2.12",
- "@backstage/test-utils": "^0.1.20",
+ "@backstage/cli": "^0.10.0",
+ "@backstage/dev-utils": "^0.2.13",
+ "@backstage/test-utils": "^0.1.22",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^13.1.8",
diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md
index b9c6601e6c..e3380082f7 100644
--- a/packages/integration/CHANGELOG.md
+++ b/packages/integration/CHANGELOG.md
@@ -1,5 +1,11 @@
# @backstage/integration
+## 0.6.10
+
+### Patch Changes
+
+- 47619da24c: Narrow the types returned by the request option functions, to only the specifics that they actually do return. The reason for this change is that a full `RequestInit` is unfortunate to return because it's different between `cross-fetch` and `node-fetch`.
+
## 0.6.9
### Patch Changes
diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md
index 4c2258779a..fcaa39b285 100644
--- a/packages/integration/api-report.md
+++ b/packages/integration/api-report.md
@@ -112,7 +112,9 @@ export function getAzureFileFetchUrl(url: string): string;
export function getAzureRequestOptions(
config: AzureIntegrationConfig,
additionalHeaders?: Record,
-): RequestInit;
+): {
+ headers: Record;
+};
// @public
export function getBitbucketDefaultBranch(
@@ -135,7 +137,9 @@ export function getBitbucketFileFetchUrl(
// @public
export function getBitbucketRequestOptions(
config: BitbucketIntegrationConfig,
-): RequestInit;
+): {
+ headers: Record;
+};
// @public
export function getGitHubFileFetchUrl(
@@ -148,7 +152,9 @@ export function getGitHubFileFetchUrl(
export function getGitHubRequestOptions(
config: GitHubIntegrationConfig,
credentials: GithubCredentials,
-): RequestInit;
+): {
+ headers: Record;
+};
// @public
export function getGitLabFileFetchUrl(
@@ -157,9 +163,9 @@ export function getGitLabFileFetchUrl(
): Promise;
// @public
-export function getGitLabRequestOptions(
- config: GitLabIntegrationConfig,
-): RequestInit;
+export function getGitLabRequestOptions(config: GitLabIntegrationConfig): {
+ headers: Record;
+};
// @public
export type GithubAppConfig = {
diff --git a/packages/integration/package.json b/packages/integration/package.json
index bd49ecc419..a6d91511c3 100644
--- a/packages/integration/package.json
+++ b/packages/integration/package.json
@@ -1,7 +1,7 @@
{
"name": "@backstage/integration",
"description": "Helpers for managing integrations towards external systems",
- "version": "0.6.9",
+ "version": "0.6.10",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -39,9 +39,9 @@
"lodash": "^4.17.21"
},
"devDependencies": {
- "@backstage/cli": "^0.8.1",
- "@backstage/config-loader": "^0.7.1",
- "@backstage/test-utils": "^0.1.20",
+ "@backstage/cli": "^0.10.0",
+ "@backstage/config-loader": "^0.8.1",
+ "@backstage/test-utils": "^0.1.22",
"@types/jest": "^26.0.7",
"@types/luxon": "^2.0.4",
"msw": "^0.35.0"
diff --git a/packages/integration/src/azure/core.ts b/packages/integration/src/azure/core.ts
index 7461954267..1d5c4c3c89 100644
--- a/packages/integration/src/azure/core.ts
+++ b/packages/integration/src/azure/core.ts
@@ -64,8 +64,8 @@ export function getAzureCommitsUrl(url: string): string {
export function getAzureRequestOptions(
config: AzureIntegrationConfig,
additionalHeaders?: Record,
-): RequestInit {
- const headers: HeadersInit = additionalHeaders
+): { headers: Record } {
+ const headers: Record = additionalHeaders
? { ...additionalHeaders }
: {};
diff --git a/packages/integration/src/bitbucket/core.ts b/packages/integration/src/bitbucket/core.ts
index afcb9fcfdb..da10152bf8 100644
--- a/packages/integration/src/bitbucket/core.ts
+++ b/packages/integration/src/bitbucket/core.ts
@@ -158,8 +158,8 @@ export function getBitbucketFileFetchUrl(
*/
export function getBitbucketRequestOptions(
config: BitbucketIntegrationConfig,
-): RequestInit {
- const headers: HeadersInit = {};
+): { headers: Record } {
+ const headers: Record = {};
if (config.token) {
headers.Authorization = `Bearer ${config.token}`;
diff --git a/packages/integration/src/github/core.ts b/packages/integration/src/github/core.ts
index 2c5e739a26..76e54fd544 100644
--- a/packages/integration/src/github/core.ts
+++ b/packages/integration/src/github/core.ts
@@ -73,8 +73,8 @@ export function getGitHubFileFetchUrl(
export function getGitHubRequestOptions(
config: GitHubIntegrationConfig,
credentials: GithubCredentials,
-): RequestInit {
- const headers: HeadersInit = {};
+): { headers: Record } {
+ const headers: Record = {};
if (chooseEndpoint(config, credentials) === 'api') {
headers.Accept = 'application/vnd.github.v3.raw';
diff --git a/packages/integration/src/gitlab/core.ts b/packages/integration/src/gitlab/core.ts
index e6f4dbc910..c5b0713340 100644
--- a/packages/integration/src/gitlab/core.ts
+++ b/packages/integration/src/gitlab/core.ts
@@ -54,9 +54,9 @@ export async function getGitLabFileFetchUrl(
* @param config - The relevant provider config
* @public
*/
-export function getGitLabRequestOptions(
- config: GitLabIntegrationConfig,
-): RequestInit {
+export function getGitLabRequestOptions(config: GitLabIntegrationConfig): {
+ headers: Record;
+} {
const { token = '' } = config;
return {
headers: {
diff --git a/packages/search-common/package.json b/packages/search-common/package.json
index e913ef66ff..51cea5a28e 100644
--- a/packages/search-common/package.json
+++ b/packages/search-common/package.json
@@ -39,7 +39,7 @@
"@backstage/types": "^0.1.1"
},
"devDependencies": {
- "@backstage/cli": "^0.8.1"
+ "@backstage/cli": "^0.10.0"
},
"jest": {
"roots": [
diff --git a/packages/storybook/.storybook/apis.js b/packages/storybook/.storybook/apis.js
index 41b8433594..4c0813a8e9 100644
--- a/packages/storybook/.storybook/apis.js
+++ b/packages/storybook/.storybook/apis.js
@@ -1,6 +1,5 @@
import {
AlertApiForwarder,
- ApiRegistry,
ErrorAlerter,
ErrorApiForwarder,
GithubAuth,
@@ -11,6 +10,7 @@ import {
OktaAuth,
Auth0Auth,
ConfigReader,
+ LocalStorageFeatureFlags,
} from '@backstage/core-app-api';
import {
@@ -25,80 +25,62 @@ import {
oktaAuthApiRef,
auth0AuthApiRef,
configApiRef,
+ featureFlagsApiRef,
} from '@backstage/core-plugin-api';
-const builder = ApiRegistry.builder();
-
-builder.add(configApiRef, new ConfigReader({}));
-
-const alertApi = builder.add(alertApiRef, new AlertApiForwarder());
-
-builder.add(errorApiRef, new ErrorAlerter(alertApi, new ErrorApiForwarder()));
-
-builder.add(identityApiRef, {
+const configApi = new ConfigReader({});
+const featureFlagsApi = new LocalStorageFeatureFlags();
+const alertApi = new AlertApiForwarder();
+const errorApi = new ErrorAlerter(alertApi, new ErrorApiForwarder());
+const identityApi = {
getUserId: () => 'guest',
getProfile: () => ({ email: 'guest@example.com' }),
getIdToken: () => undefined,
signOut: async () => {},
+};
+const oauthRequestApi = new OAuthRequestManager();
+const googleAuthApi = GoogleAuth.create({
+ apiOrigin: 'http://localhost:7007',
+ basePath: '/auth/',
+ oauthRequestApi,
+});
+const githubAuthApi = GithubAuth.create({
+ apiOrigin: 'http://localhost:7007',
+ basePath: '/auth/',
+ oauthRequestApi,
+});
+const gitlabAuthApi = GitlabAuth.create({
+ apiOrigin: 'http://localhost:7007',
+ basePath: '/auth/',
+ oauthRequestApi,
+});
+const oktaAuthApi = OktaAuth.create({
+ apiOrigin: 'http://localhost:7007',
+ basePath: '/auth/',
+ oauthRequestApi,
+});
+const auth0AuthApi = Auth0Auth.create({
+ apiOrigin: 'http://localhost:7007',
+ basePath: '/auth/',
+ oauthRequestApi,
+});
+const oauth2Api = OAuth2.create({
+ apiOrigin: 'http://localhost:7007',
+ basePath: '/auth/',
+ oauthRequestApi,
});
-const oauthRequestApi = builder.add(
- oauthRequestApiRef,
- new OAuthRequestManager(),
-);
-
-builder.add(
- googleAuthApiRef,
- GoogleAuth.create({
- apiOrigin: 'http://localhost:7000',
- basePath: '/auth/',
- oauthRequestApi,
- }),
-);
-
-builder.add(
- githubAuthApiRef,
- GithubAuth.create({
- apiOrigin: 'http://localhost:7000',
- basePath: '/auth/',
- oauthRequestApi,
- }),
-);
-
-builder.add(
- gitlabAuthApiRef,
- GitlabAuth.create({
- apiOrigin: 'http://localhost:7000',
- basePath: '/auth/',
- oauthRequestApi,
- }),
-);
-
-builder.add(
- oktaAuthApiRef,
- OktaAuth.create({
- apiOrigin: 'http://localhost:7000',
- basePath: '/auth/',
- oauthRequestApi,
- }),
-);
-
-builder.add(
- auth0AuthApiRef,
- Auth0Auth.create({
- apiOrigin: 'http://localhost:7000',
- basePath: '/auth/',
- oauthRequestApi,
- }),
-);
-
-builder.add(
- oauth2ApiRef,
- OAuth2.create({
- apiOrigin: 'http://localhost:7000',
- basePath: '/auth/',
- oauthRequestApi,
- }),
-);
-
-export const apis = builder.build();
+export const apis = [
+ [configApiRef, configApi],
+ [featureFlagsApiRef, featureFlagsApi],
+ [alertApiRef, alertApi],
+ [errorApiRef, errorApi],
+ [identityApiRef, identityApi],
+ [oauthRequestApiRef, oauthRequestApi],
+ [googleAuthApiRef, googleAuthApi],
+ [githubAuthApiRef, githubAuthApi],
+ [gitlabAuthApiRef, gitlabAuthApi],
+ [oktaAuthApiRef, oktaAuthApi],
+ [auth0AuthApiRef, auth0AuthApi],
+ [oauth2ApiRef, oauth2Api],
+];
diff --git a/packages/storybook/.storybook/preview.js b/packages/storybook/.storybook/preview.js
index 6186b82ca5..2b1a4af329 100644
--- a/packages/storybook/.storybook/preview.js
+++ b/packages/storybook/.storybook/preview.js
@@ -6,17 +6,17 @@ import { useDarkMode } from 'storybook-dark-mode';
import { apis } from './apis';
import { Content, AlertDisplay } from '@backstage/core-components';
-import { ApiProvider } from '@backstage/core-app-api';
+import { TestApiProvider } from '@backstage/test-utils';
addDecorator(story => (
-
+ {story()}
-
+
));
addParameters({
diff --git a/packages/techdocs-cli/.snyk b/packages/techdocs-cli/.snyk
new file mode 100644
index 0000000000..cfb30e5aa7
--- /dev/null
+++ b/packages/techdocs-cli/.snyk
@@ -0,0 +1,17 @@
+# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
+version: v1.22.1
+# ignores vulnerabilities until expiry date; change duration by modifying expiry date
+ignore:
+ SNYK-JS-BROWSERSLIST-1090194:
+ - '*':
+ reason: Developer tools are not a valid target for ReDoS attacks
+ expires: 2022-05-20T00:00:00.000Z
+ created: 2021-11-20T00:00:00.000Z
+
+ SNYK-JS-IMMER-1540542:
+ - '*':
+ reason: Prototype pollution is not an effective attack against a CLI as it already executes arbitrary code
+ expires: 2022-05-20T00:00:00.000Z
+ created: 2021-11-20T00:00:00.000Z
+
+patch: {}
diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md
index 0f7c6be52f..d07361e915 100644
--- a/packages/techdocs-cli/CHANGELOG.md
+++ b/packages/techdocs-cli/CHANGELOG.md
@@ -1,5 +1,25 @@
# @techdocs/cli
+## 0.8.7
+
+### Patch Changes
+
+- e7230ef814: Bump react-dev-utils to v12
+- Updated dependencies
+ - @backstage/backend-common@0.9.12
+
+## 0.8.6
+
+### Patch Changes
+
+- e21e3c6102: Bumping minimum requirements for `dockerode` and `testcontainers`
+- 1578ad341b: Add support for specifying bucketRootPath for AWS and GCS publishers
+- f2694e3750: Adds ability to use encrypted S3 buckets by utilizing the SSE option in the AWS SDK
+- Updated dependencies
+ - @backstage/catalog-model@0.9.7
+ - @backstage/backend-common@0.9.10
+ - @backstage/techdocs-common@0.10.7
+
## 0.8.5
### Patch Changes
diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json
index a24a1be0f8..34d67d4dc9 100644
--- a/packages/techdocs-cli/package.json
+++ b/packages/techdocs-cli/package.json
@@ -1,7 +1,7 @@
{
"name": "@techdocs/cli",
"description": "Utility CLI for managing TechDocs sites in Backstage.",
- "version": "0.8.5",
+ "version": "0.8.7",
"private": false,
"publishConfig": {
"access": "public"
@@ -32,7 +32,7 @@
"techdocs-cli": "bin/techdocs-cli"
},
"devDependencies": {
- "@backstage/cli": "^0.8.0",
+ "@backstage/cli": "^0.10.0",
"@types/commander": "^2.12.2",
"@types/fs-extra": "^9.0.6",
"@types/http-proxy": "^1.17.4",
@@ -41,7 +41,7 @@
"@types/react-dev-utils": "^9.0.4",
"@types/serve-handler": "^6.1.0",
"@types/webpack-env": "^1.15.3",
- "embedded-techdocs-app": "0.0.0",
+ "embedded-techdocs-app": "0.2.55",
"find-process": "^1.4.5",
"nodemon": "^2.0.2",
"ts-node": "^10.0.0"
@@ -56,16 +56,16 @@
"ext": "ts"
},
"dependencies": {
- "@backstage/backend-common": "^0.9.7",
- "@backstage/catalog-model": "^0.9.5",
+ "@backstage/backend-common": "^0.9.12",
+ "@backstage/catalog-model": "^0.9.7",
"@backstage/config": "^0.1.10",
- "@backstage/techdocs-common": "^0.10.4",
- "@types/dockerode": "^3.2.1",
+ "@backstage/techdocs-common": "^0.10.7",
+ "@types/dockerode": "^3.3.0",
"commander": "^6.1.0",
- "dockerode": "^3.2.1",
+ "dockerode": "^3.3.1",
"fs-extra": "^9.0.1",
"http-proxy": "^1.18.1",
- "react-dev-utils": "^11.0.4",
+ "react-dev-utils": "^12.0.0-next.47",
"serve-handler": "^6.1.3",
"winston": "^3.2.1"
}
diff --git a/packages/techdocs-cli/src/commands/index.ts b/packages/techdocs-cli/src/commands/index.ts
index 280072cfd9..d4f41cf7a3 100644
--- a/packages/techdocs-cli/src/commands/index.ts
+++ b/packages/techdocs-cli/src/commands/index.ts
@@ -161,6 +161,7 @@ export function registerCommands(program: CommanderStatic) {
'--awsEndpoint ',
'Optional AWS endpoint to send requests to.',
)
+ .option('--awsS3sse ', 'Optional AWS S3 Server Side Encryption.')
.option(
'--awsS3ForcePathStyle',
'Optional AWS S3 option to force path style.',
diff --git a/packages/techdocs-cli/src/commands/serve/serve.ts b/packages/techdocs-cli/src/commands/serve/serve.ts
index 1182c850b3..4d71de3e52 100644
--- a/packages/techdocs-cli/src/commands/serve/serve.ts
+++ b/packages/techdocs-cli/src/commands/serve/serve.ts
@@ -36,7 +36,7 @@ export default async function serve(cmd: Command) {
// a backstage app, we define app.baseUrl in the app-config.yaml.
// Hence, it is complicated to make this configurable.
const backstagePort = 3000;
- const backstageBackendPort = 7000;
+ const backstageBackendPort = 7007;
const mkdocsDockerAddr = `http://0.0.0.0:${cmd.mkdocsPort}`;
const mkdocsLocalAddr = `http://127.0.0.1:${cmd.mkdocsPort}`;
diff --git a/packages/techdocs-cli/src/lib/PublisherConfig.test.ts b/packages/techdocs-cli/src/lib/PublisherConfig.test.ts
index 519972c03d..88f542c049 100644
--- a/packages/techdocs-cli/src/lib/PublisherConfig.test.ts
+++ b/packages/techdocs-cli/src/lib/PublisherConfig.test.ts
@@ -80,6 +80,20 @@ describe('getValidPublisherConfig', () => {
actualConfig.getString('techdocs.publisher.awsS3.bucketRootPath'),
).toBe('backstage-data/techdocs');
});
+
+ it('should return valid ConfigReader with SSE option', () => {
+ const config = {
+ publisherType: 'awsS3',
+ storageName: 'someStorageName',
+ awsS3sse: 'aws:kms',
+ } as unknown as Command;
+
+ const actualConfig = PublisherConfig.getValidConfig(config);
+ expect(actualConfig.getString('techdocs.publisher.type')).toBe('awsS3');
+ expect(actualConfig.getString('techdocs.publisher.awsS3.sse')).toBe(
+ 'aws:kms',
+ );
+ });
});
describe('for openStackSwift', () => {
diff --git a/packages/techdocs-cli/src/lib/PublisherConfig.ts b/packages/techdocs-cli/src/lib/PublisherConfig.ts
index e855d573ce..c18b9ff588 100644
--- a/packages/techdocs-cli/src/lib/PublisherConfig.ts
+++ b/packages/techdocs-cli/src/lib/PublisherConfig.ts
@@ -55,9 +55,9 @@ export class PublisherConfig {
return new ConfigReader({
// This backend config is not used at all. Just something needed a create a mock discovery instance.
backend: {
- baseUrl: 'http://localhost:7000',
+ baseUrl: 'http://localhost:7007',
listen: {
- port: 7000,
+ port: 7007,
},
},
techdocs: {
@@ -89,6 +89,7 @@ export class PublisherConfig {
...(cmd.awsRoleArn && { credentials: { roleArn: cmd.awsRoleArn } }),
...(cmd.awsEndpoint && { endpoint: cmd.awsEndpoint }),
...(cmd.awsS3ForcePathStyle && { s3ForcePathStyle: true }),
+ ...(cmd.awsS3sse && { sse: cmd.awsS3sse }),
},
};
}
diff --git a/packages/techdocs-common/CHANGELOG.md b/packages/techdocs-common/CHANGELOG.md
index 87e9e12193..386c584196 100644
--- a/packages/techdocs-common/CHANGELOG.md
+++ b/packages/techdocs-common/CHANGELOG.md
@@ -1,5 +1,37 @@
# @backstage/techdocs-common
+## 0.10.8
+
+### Patch Changes
+
+- bab752e2b3: Change default port of backend from 7000 to 7007.
+
+ This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start.
+
+ You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values:
+
+ ```
+ backend:
+ listen: 0.0.0.0:7123
+ baseUrl: http://localhost:7123
+ ```
+
+ More information can be found here: https://backstage.io/docs/conf/writing
+
+- Updated dependencies
+ - @backstage/errors@0.1.5
+ - @backstage/backend-common@0.9.11
+
+## 0.10.7
+
+### Patch Changes
+
+- 0b60a051c9: Added OpenStack Swift case migration support.
+- 9e64a7ac1e: Allow amazon web services s3 buckets to pass an server side encryption configuration so they can publish to encrypted buckets
+- Updated dependencies
+ - @backstage/catalog-model@0.9.7
+ - @backstage/backend-common@0.9.10
+
## 0.10.6
### Patch Changes
@@ -855,7 +887,7 @@
Based on the config `techdocs.publisher.type`, the publisher could be either Local publisher or Google Cloud Storage publisher.
- 4. `techdocs.storageUrl` is now a required config. Should be `http://localhost:7000/api/techdocs/static/docs` in most setups.
+ 4. `techdocs.storageUrl` is now a required config. Should be `http://localhost:7007/api/techdocs/static/docs` in most setups.
5. Parts of `@backstage/plugin-techdocs-backend` have been moved to a new package `@backstage/techdocs-common` to generate docs. Also to publish docs
to-and-fro between TechDocs and a storage (either local or external). However, a Backstage app does NOT need to import the `techdocs-common` package -
diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json
index b44e68374d..e6956b87e6 100644
--- a/packages/techdocs-common/package.json
+++ b/packages/techdocs-common/package.json
@@ -1,7 +1,7 @@
{
"name": "@backstage/techdocs-common",
"description": "Common functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli",
- "version": "0.10.6",
+ "version": "0.10.8",
"main": "src/index.ts",
"types": "src/index.ts",
"private": false,
@@ -38,12 +38,12 @@
"dependencies": {
"@azure/identity": "^1.5.0",
"@azure/storage-blob": "^12.5.0",
- "@backstage/backend-common": "^0.9.9",
- "@backstage/catalog-model": "^0.9.6",
+ "@backstage/backend-common": "^0.9.12",
+ "@backstage/catalog-model": "^0.9.7",
"@backstage/config": "^0.1.11",
- "@backstage/errors": "^0.1.4",
+ "@backstage/errors": "^0.1.5",
"@backstage/search-common": "^0.2.1",
- "@backstage/integration": "^0.6.9",
+ "@backstage/integration": "^0.6.10",
"@google-cloud/storage": "^5.6.0",
"@trendyol-js/openstack-swift-sdk": "^0.0.5",
"@types/express": "^4.17.6",
@@ -60,7 +60,7 @@
"winston": "^3.2.1"
},
"devDependencies": {
- "@backstage/cli": "^0.8.2",
+ "@backstage/cli": "^0.10.0",
"@types/fs-extra": "^9.0.5",
"@types/js-yaml": "^4.0.0",
"@types/mime-types": "^2.1.0",
diff --git a/packages/techdocs-common/src/stages/publish/awsS3.test.ts b/packages/techdocs-common/src/stages/publish/awsS3.test.ts
index ff88f12939..1f49668d35 100644
--- a/packages/techdocs-common/src/stages/publish/awsS3.test.ts
+++ b/packages/techdocs-common/src/stages/publish/awsS3.test.ts
@@ -54,7 +54,7 @@ const createPublisherFromConfig = ({
} = {}) => {
const mockConfig = new ConfigReader({
techdocs: {
- requestUrl: 'http://localhost:7000',
+ requestUrl: 'http://localhost:7007',
publisher: {
type: 'awsS3',
awsS3: {
diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts
index 1bf3586266..a2503bd7b2 100644
--- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts
+++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.test.ts
@@ -51,7 +51,7 @@ const createPublisherFromConfig = ({
} = {}) => {
const config = new ConfigReader({
techdocs: {
- requestUrl: 'http://localhost:7000',
+ requestUrl: 'http://localhost:7007',
publisher: {
type: 'azureBlobStorage',
azureBlobStorage: {
diff --git a/packages/techdocs-common/src/stages/publish/googleStorage.test.ts b/packages/techdocs-common/src/stages/publish/googleStorage.test.ts
index c085594325..a52fd0f0bd 100644
--- a/packages/techdocs-common/src/stages/publish/googleStorage.test.ts
+++ b/packages/techdocs-common/src/stages/publish/googleStorage.test.ts
@@ -52,7 +52,7 @@ const createPublisherFromConfig = ({
} = {}) => {
const config = new ConfigReader({
techdocs: {
- requestUrl: 'http://localhost:7000',
+ requestUrl: 'http://localhost:7007',
publisher: {
type: 'googleGcs',
googleGcs: {
diff --git a/packages/techdocs-common/src/stages/publish/local.test.ts b/packages/techdocs-common/src/stages/publish/local.test.ts
index de56580e02..761de03c5a 100644
--- a/packages/techdocs-common/src/stages/publish/local.test.ts
+++ b/packages/techdocs-common/src/stages/publish/local.test.ts
@@ -39,7 +39,7 @@ const createMockEntity = (annotations = {}, lowerCase = false) => {
};
const testDiscovery: jest.Mocked = {
- getBaseUrl: jest.fn().mockResolvedValue('http://localhost:7000/api/techdocs'),
+ getBaseUrl: jest.fn().mockResolvedValue('http://localhost:7007/api/techdocs'),
getExternalBaseUrl: jest.fn(),
};
diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts
index 8dde687997..c2236fb880 100644
--- a/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts
+++ b/packages/techdocs-common/src/stages/publish/openStackSwift.test.ts
@@ -84,7 +84,7 @@ beforeEach(() => {
mockFs.restore();
const mockConfig = new ConfigReader({
techdocs: {
- requestUrl: 'http://localhost:7000',
+ requestUrl: 'http://localhost:7007',
publisher: {
type: 'openStackSwift',
openStackSwift: {
@@ -114,7 +114,7 @@ describe('OpenStackSwiftPublish', () => {
it('should reject incorrect config', async () => {
const mockConfig = new ConfigReader({
techdocs: {
- requestUrl: 'http://localhost:7000',
+ requestUrl: 'http://localhost:7007',
publisher: {
type: 'openStackSwift',
openStackSwift: {
diff --git a/packages/techdocs-common/src/stages/publish/publish.test.ts b/packages/techdocs-common/src/stages/publish/publish.test.ts
index 2671ef7b47..a47da828a7 100644
--- a/packages/techdocs-common/src/stages/publish/publish.test.ts
+++ b/packages/techdocs-common/src/stages/publish/publish.test.ts
@@ -27,7 +27,7 @@ import { OpenStackSwiftPublish } from './openStackSwift';
const logger = getVoidLogger();
const discovery: jest.Mocked = {
- getBaseUrl: jest.fn().mockResolvedValueOnce('http://localhost:7000'),
+ getBaseUrl: jest.fn().mockResolvedValueOnce('http://localhost:7007'),
getExternalBaseUrl: jest.fn(),
};
@@ -39,7 +39,7 @@ describe('Publisher', () => {
it('should create local publisher by default', async () => {
const mockConfig = new ConfigReader({
techdocs: {
- requestUrl: 'http://localhost:7000',
+ requestUrl: 'http://localhost:7007',
},
});
@@ -53,7 +53,7 @@ describe('Publisher', () => {
it('should create local publisher from config', async () => {
const mockConfig = new ConfigReader({
techdocs: {
- requestUrl: 'http://localhost:7000',
+ requestUrl: 'http://localhost:7007',
publisher: {
type: 'local',
},
@@ -70,7 +70,7 @@ describe('Publisher', () => {
it('should create google gcs publisher from config', async () => {
const mockConfig = new ConfigReader({
techdocs: {
- requestUrl: 'http://localhost:7000',
+ requestUrl: 'http://localhost:7007',
publisher: {
type: 'googleGcs',
googleGcs: {
@@ -91,7 +91,7 @@ describe('Publisher', () => {
it('should create AWS S3 publisher from config', async () => {
const mockConfig = new ConfigReader({
techdocs: {
- requestUrl: 'http://localhost:7000',
+ requestUrl: 'http://localhost:7007',
publisher: {
type: 'awsS3',
awsS3: {
@@ -115,7 +115,7 @@ describe('Publisher', () => {
it('should create Azure Blob Storage publisher from config', async () => {
const mockConfig = new ConfigReader({
techdocs: {
- requestUrl: 'http://localhost:7000',
+ requestUrl: 'http://localhost:7007',
publisher: {
type: 'azureBlobStorage',
azureBlobStorage: {
@@ -143,7 +143,7 @@ describe('Publisher', () => {
const mockConfig = new ConfigReader({
techdocs: {
- requestUrl: 'http://localhost:7000',
+ requestUrl: 'http://localhost:7007',
publisher: {
type: 'azureBlobStorage',
azureBlobStorage: {
@@ -166,7 +166,7 @@ describe('Publisher', () => {
it('should create Open Stack Swift publisher from config', async () => {
const mockConfig = new ConfigReader({
techdocs: {
- requestUrl: 'http://localhost:7000',
+ requestUrl: 'http://localhost:7007',
publisher: {
type: 'openStackSwift',
openStackSwift: {
diff --git a/packages/test-utils-core/.npmrc b/packages/test-utils-core/.npmrc
deleted file mode 100644
index 214c29d139..0000000000
--- a/packages/test-utils-core/.npmrc
+++ /dev/null
@@ -1 +0,0 @@
-registry=https://registry.npmjs.org/
diff --git a/packages/test-utils-core/.snyk b/packages/test-utils-core/.snyk
deleted file mode 100644
index f368a3ddb9..0000000000
--- a/packages/test-utils-core/.snyk
+++ /dev/null
@@ -1,10 +0,0 @@
-# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
-version: v1.22.1
-# ignores vulnerabilities until expiry date; change duration by modifying expiry date
-ignore:
- SNYK-JS-ANSIREGEX-1583908:
- - '*':
- reason: This only exposes the user's machine or build worker to a ReDos attack, which we don't consider an issue
- expires: 2022-03-06T17:18:55.019Z
- created: 2021-09-06T17:18:55.027Z
-patch: {}
diff --git a/packages/test-utils-core/CHANGELOG.md b/packages/test-utils-core/CHANGELOG.md
deleted file mode 100644
index 35bdf1b2c6..0000000000
--- a/packages/test-utils-core/CHANGELOG.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# @backstage/test-utils-core
-
-## 0.1.4
-
-### Patch Changes
-
-- bb12aae352: Migrates all utility methods from `test-utils-core` into `test-utils` and delete exports from the old package.
- This should have no impact since this package is considered internal and have no usages outside core packages.
-
- Notable changes are that the testing tool `msw.setupDefaultHandlers()` have been deprecated in favour of `setupRequestMockHandlers()`.
-
-## 0.1.3
-
-### Patch Changes
-
-- ca0559444c: Avoid usage of `.to*Case()`, preferring `.toLocale*Case('en-US')` instead.
-
-## 0.1.2
-
-### Patch Changes
-
-- 56c773909: Switched `@types/react` dependency to request `*` rather than a specific version.
diff --git a/packages/test-utils-core/README.md b/packages/test-utils-core/README.md
deleted file mode 100644
index 63b6ece0a6..0000000000
--- a/packages/test-utils-core/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# @backstage/test-utils-core
-
-This package provides utilities for testing the Backstage core packages.
-
-## Installation
-
-This package should not be used directly, use `@backstage/test-utils` instead. All exports from this
-package are re-exported by `@backstage/test-utils`.
-
-The reason this package exists is to allow the Backstage core packages to use the testing utils exposed in this package. Since `@backstage/test-utils` needs to depend on `@backstage/core`, core is not able to use those test-utils. We put any test-utils that don't need to depend on other Backstage packages in this package, in order for them to be usable by any other `@backstage` packages.
-
-## Documentation
-
-- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md)
-- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md)
diff --git a/packages/test-utils-core/api-report.md b/packages/test-utils-core/api-report.md
deleted file mode 100644
index f21c749727..0000000000
--- a/packages/test-utils-core/api-report.md
+++ /dev/null
@@ -1,7 +0,0 @@
-## API Report File for "@backstage/test-utils-core"
-
-> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
-
-```ts
-// (No @packageDocumentation comment for this package)
-```
diff --git a/packages/test-utils-core/package.json b/packages/test-utils-core/package.json
deleted file mode 100644
index ebe6decddb..0000000000
--- a/packages/test-utils-core/package.json
+++ /dev/null
@@ -1,39 +0,0 @@
-{
- "name": "@backstage/test-utils-core",
- "description": "Utilities to test Backstage core",
- "version": "0.1.4",
- "private": false,
- "publishConfig": {
- "access": "public",
- "main": "dist/index.esm.js",
- "types": "dist/index.d.ts"
- },
- "homepage": "https://backstage.io",
- "repository": {
- "type": "git",
- "url": "https://github.com/backstage/backstage",
- "directory": "packages/test-utils-core"
- },
- "keywords": [
- "backstage"
- ],
- "license": "Apache-2.0",
- "main": "src/index.ts",
- "types": "src/index.ts",
- "scripts": {
- "build": "backstage-cli build --outputs types,esm",
- "lint": "backstage-cli lint",
- "test": "backstage-cli test --passWithNoTests",
- "prepack": "backstage-cli prepack",
- "postpack": "backstage-cli postpack",
- "clean": "backstage-cli clean"
- },
- "dependencies": {},
- "devDependencies": {
- "@types/jest": "^26.0.7",
- "@types/node": "^14.14.32"
- },
- "files": [
- "dist"
- ]
-}
diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md
index 6fcacc30fa..fd8ce852db 100644
--- a/packages/test-utils/CHANGELOG.md
+++ b/packages/test-utils/CHANGELOG.md
@@ -1,5 +1,82 @@
# @backstage/test-utils
+## 0.1.23
+
+### Patch Changes
+
+- 000190de69: The `ApiRegistry` from `@backstage/core-app-api` class has been deprecated and will be removed in a future release. To replace it, we have introduced two new helpers that are exported from `@backstage/test-utils`, namely `TestApiProvider` and `TestApiRegistry`.
+
+ These two new helpers are more tailored for writing tests and development setups, as they allow for partial implementations of each of the APIs.
+
+ When migrating existing code it is typically best to prefer usage of `TestApiProvider` when possible, so for example the following code:
+
+ ```tsx
+ render(
+
+ {...}
+
+ )
+ ```
+
+ Would be migrated to this:
+
+ ```tsx
+ render(
+
+ {...}
+
+ )
+ ```
+
+ In cases where the `ApiProvider` is used in a more standalone way, for example to reuse a set of APIs across multiple tests, the `TestApiRegistry` can be used instead. Note that the `TestApiRegistry` only has a single static factory method, `.from()`, and it is slightly different from the existing `.from()` method on `ApiRegistry` in that it doesn't require the API pairs to be wrapped in an outer array.
+
+ Usage that looks like this:
+
+ ```ts
+ const apis = ApiRegistry.with(
+ identityApiRef,
+ mockIdentityApi as unknown as IdentityApi,
+ ).with(configApiRef, new ConfigReader({}));
+ ```
+
+ OR like this:
+
+ ```ts
+ const apis = ApiRegistry.from([
+ [identityApiRef, mockIdentityApi as unknown as IdentityApi],
+ [configApiRef, new ConfigReader({})],
+ ]);
+ ```
+
+ Would be migrated to this:
+
+ ```ts
+ const apis = TestApiRegistry.from(
+ [identityApiRef, mockIdentityApi],
+ [configApiRef, new ConfigReader({})],
+ );
+ ```
+
+ If your app is still using the `ApiRegistry` to construct the `apis` for `createApp`, we recommend that you move over to use the new method of supplying API factories instead, using `createApiFactory`.
+
+- Updated dependencies
+ - @backstage/core-app-api@0.1.23
+ - @backstage/core-plugin-api@0.2.1
+
+## 0.1.22
+
+### Patch Changes
+
+- 0b1de52732: Migrated to using new `ErrorApiError` and `ErrorApiErrorContext` names.
+- 2dd2a7b2cc: Migrated to using `createSpecializedApp`.
+- Updated dependencies
+ - @backstage/core-plugin-api@0.2.0
+ - @backstage/core-app-api@0.1.21
+
## 0.1.21
### Patch Changes
diff --git a/packages/test-utils/api-report.md b/packages/test-utils/api-report.md
index 62a8d9f601..83c209122d 100644
--- a/packages/test-utils/api-report.md
+++ b/packages/test-utils/api-report.md
@@ -5,6 +5,8 @@
```ts
import { AnalyticsApi } from '@backstage/core-plugin-api';
import { AnalyticsEvent } from '@backstage/core-plugin-api';
+import { ApiHolder } from '@backstage/core-plugin-api';
+import { ApiRef } from '@backstage/core-plugin-api';
import { ComponentType } from 'react';
import { ErrorApi } from '@backstage/core-plugin-api';
import { ErrorApiError } from '@backstage/core-plugin-api';
@@ -175,6 +177,26 @@ export function setupRequestMockHandlers(worker: {
// @public
export type SyncLogCollector = () => void;
+// @public
+export const TestApiProvider: ({
+ apis,
+ children,
+}: TestApiProviderProps) => JSX.Element;
+
+// @public
+export type TestApiProviderProps = {
+ apis: readonly [...TestApiProviderPropsApiPairs];
+ children: ReactNode;
+};
+
+// @public
+export class TestApiRegistry implements ApiHolder {
+ static from(
+ ...apis: readonly [...TestApiProviderPropsApiPairs]
+ ): TestApiRegistry;
+ get(api: ApiRef): T | undefined;
+}
+
// @public
export type TestAppOptions = {
routeEntries?: string[];
diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json
index 6f5f78de8c..87c5b346f6 100644
--- a/packages/test-utils/package.json
+++ b/packages/test-utils/package.json
@@ -1,7 +1,7 @@
{
"name": "@backstage/test-utils",
"description": "Utilities to test Backstage plugins and apps.",
- "version": "0.1.21",
+ "version": "0.1.23",
"private": false,
"publishConfig": {
"access": "public",
@@ -29,9 +29,9 @@
"clean": "backstage-cli clean"
},
"dependencies": {
- "@backstage/core-app-api": "^0.1.20",
- "@backstage/core-plugin-api": "^0.1.13",
- "@backstage/theme": "^0.2.13",
+ "@backstage/core-app-api": "^0.1.24",
+ "@backstage/core-plugin-api": "^0.2.2",
+ "@backstage/theme": "^0.2.14",
"@backstage/types": "^0.1.1",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.11.2",
@@ -46,7 +46,7 @@
"zen-observable": "^0.8.15"
},
"devDependencies": {
- "@backstage/cli": "^0.8.2",
+ "@backstage/cli": "^0.10.0",
"@types/jest": "^26.0.7",
"@types/node": "^14.14.32"
},
diff --git a/packages/test-utils/src/testUtils/TestApiProvider.test.tsx b/packages/test-utils/src/testUtils/TestApiProvider.test.tsx
new file mode 100644
index 0000000000..1b981b15ec
--- /dev/null
+++ b/packages/test-utils/src/testUtils/TestApiProvider.test.tsx
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import { createApiRef, useApiHolder } from '@backstage/core-plugin-api';
+import { TestApiProvider, TestApiRegistry } from './TestApiProvider';
+import { render, screen } from '@testing-library/react';
+
+const xApiRef = createApiRef<{ a: string; b: number }>({
+ id: 'x',
+});
+const yApiRef = createApiRef({
+ id: 'y',
+});
+
+function Verifier() {
+ const holder = useApiHolder();
+ const x = holder.get(xApiRef);
+ const y = holder.get(yApiRef);
+
+ return (
+