diff --git a/.changeset/afraid-items-drum.md b/.changeset/afraid-items-drum.md
new file mode 100644
index 0000000000..94ddd608cd
--- /dev/null
+++ b/.changeset/afraid-items-drum.md
@@ -0,0 +1,5 @@
+---
+'@backstage/cli-node': patch
+---
+
+Updated to use new utilities from `@backstage/cli-common`.
diff --git a/.changeset/alert-api-replay.md b/.changeset/alert-api-replay.md
new file mode 100644
index 0000000000..b41ec091a3
--- /dev/null
+++ b/.changeset/alert-api-replay.md
@@ -0,0 +1,5 @@
+---
+'@backstage/core-app-api': patch
+---
+
+Added replay functionality to `AlertApiForwarder` to buffer and replay recent alerts to new subscribers, preventing missed alerts that were posted before subscription.
diff --git a/.changeset/all-socks-taste.md b/.changeset/all-socks-taste.md
new file mode 100644
index 0000000000..377782c2f9
--- /dev/null
+++ b/.changeset/all-socks-taste.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder-backend-module-gitlab': minor
+---
+
+In the `gitlabRepoPush` action, add 'auto' possibility for `commitAction` input.
diff --git a/.changeset/better-hats-cross.md b/.changeset/better-hats-cross.md
deleted file mode 100644
index 99444b7fbf..0000000000
--- a/.changeset/better-hats-cross.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/backend-defaults': patch
----
-
-Adjusted the log line wording of task worker starting
diff --git a/.changeset/better-steaks-act.md b/.changeset/better-steaks-act.md
deleted file mode 100644
index b3ce601245..0000000000
--- a/.changeset/better-steaks-act.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-The templates executed with the `yarn new` command now supports templating filenames.
diff --git a/.changeset/bumpy-planets-go.md b/.changeset/bumpy-planets-go.md
new file mode 100644
index 0000000000..5477711c1a
--- /dev/null
+++ b/.changeset/bumpy-planets-go.md
@@ -0,0 +1,7 @@
+---
+'@backstage/ui': patch
+---
+
+Fixed Checkbox indicator showing checkmark color when unchecked.
+
+Affected components: Checkbox
diff --git a/.changeset/chatty-sides-wear.md b/.changeset/chatty-sides-wear.md
new file mode 100644
index 0000000000..33937a1286
--- /dev/null
+++ b/.changeset/chatty-sides-wear.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog-graph': patch
+---
+
+Fixed zoom out description (replace `&` with &)
diff --git a/.changeset/chilly-bikes-rule.md b/.changeset/chilly-bikes-rule.md
new file mode 100644
index 0000000000..894999cf56
--- /dev/null
+++ b/.changeset/chilly-bikes-rule.md
@@ -0,0 +1,5 @@
+---
+'@techdocs/cli': patch
+---
+
+Updated to use new utilities from `@backstage/cli-common`.
diff --git a/.changeset/chilly-hotels-walk.md b/.changeset/chilly-hotels-walk.md
new file mode 100644
index 0000000000..e9b1ff66ba
--- /dev/null
+++ b/.changeset/chilly-hotels-walk.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-search-backend-node': minor
+---
+
+Improving method that search tokenizer breaks apart entity names
diff --git a/.changeset/chilly-waves-relate.md b/.changeset/chilly-waves-relate.md
new file mode 100644
index 0000000000..2e78f9245e
--- /dev/null
+++ b/.changeset/chilly-waves-relate.md
@@ -0,0 +1,11 @@
+---
+'@backstage/cli': minor
+---
+
+Switched the default module resolution to `bundler` and the `module` setting to `ES2020`.
+
+You may need to bump some dependencies as part of this change and fix imports in code. The most common source of this is that type checking will now consider the `exports` field in `package.json` when resolving imports. This in turn can break older versions of packages that had incompatible `exports` fields. Generally these issues will have already been fixed in the upstream packages.
+
+You might be tempted to use `--skipLibCheck` to hide issues due to this change, but it will weaken the type safety of your project. If you run into a large number of issues and want to keep the old behavior, you can reset the `moduleResolution` and `module` settings your own `tsconfig.json` file to `node` and `ESNext` respectively. But keep in mind that the `node` option will be removed in future versions of TypeScript.
+
+A future version of Backstage will make these new settings mandatory, as we move to rely on the `exports` field for type resolution in packages, rather than the `typesVersions` field.
diff --git a/.changeset/clean-toys-reply.md b/.changeset/clean-toys-reply.md
new file mode 100644
index 0000000000..66002903ab
--- /dev/null
+++ b/.changeset/clean-toys-reply.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-auth-node': patch
+---
+
+remove leading dot in auth cookie cleanup call
diff --git a/.changeset/common-coins-stare.md b/.changeset/common-coins-stare.md
new file mode 100644
index 0000000000..0bd0078cf9
--- /dev/null
+++ b/.changeset/common-coins-stare.md
@@ -0,0 +1,7 @@
+---
+'@backstage/ui': patch
+---
+
+Fixed `ButtonIcon` incorrectly applying `className` to inner elements instead of only the root element.
+
+Affected components: ButtonIcon
diff --git a/.changeset/crazy-hornets-spend.md b/.changeset/crazy-hornets-spend.md
new file mode 100644
index 0000000000..4db99675ac
--- /dev/null
+++ b/.changeset/crazy-hornets-spend.md
@@ -0,0 +1,11 @@
+---
+'@backstage/plugin-catalog-graph': patch
+'@backstage/plugin-api-docs': patch
+'@backstage/plugin-techdocs': patch
+'@backstage/plugin-catalog': patch
+'@backstage/plugin-search': patch
+'@backstage/plugin-app': patch
+'@backstage/plugin-org': patch
+---
+
+Minor extension type updates after frontend API bump
diff --git a/.changeset/create-app-1764689798.md b/.changeset/create-app-1764689798.md
new file mode 100644
index 0000000000..b50d431d4b
--- /dev/null
+++ b/.changeset/create-app-1764689798.md
@@ -0,0 +1,5 @@
+---
+'@backstage/create-app': patch
+---
+
+Bumped create-app version.
diff --git a/.changeset/dependabot-0d8f6de.md b/.changeset/dependabot-0d8f6de.md
new file mode 100644
index 0000000000..b0a3955f2d
--- /dev/null
+++ b/.changeset/dependabot-0d8f6de.md
@@ -0,0 +1,47 @@
+---
+'@backstage/backend-defaults': patch
+'@backstage/backend-dynamic-feature-service': patch
+'@backstage/backend-openapi-utils': patch
+'@backstage/backend-test-utils': patch
+'@backstage/cli': patch
+'@backstage/plugin-app-backend': patch
+'@backstage/plugin-app-node': patch
+'@backstage/plugin-auth-backend-module-atlassian-provider': patch
+'@backstage/plugin-auth-backend-module-auth0-provider': patch
+'@backstage/plugin-auth-backend-module-aws-alb-provider': patch
+'@backstage/plugin-auth-backend-module-azure-easyauth-provider': patch
+'@backstage/plugin-auth-backend-module-bitbucket-provider': patch
+'@backstage/plugin-auth-backend-module-cloudflare-access-provider': patch
+'@backstage/plugin-auth-backend-module-gcp-iap-provider': patch
+'@backstage/plugin-auth-backend-module-gitlab-provider': patch
+'@backstage/plugin-auth-backend-module-guest-provider': patch
+'@backstage/plugin-auth-backend-module-microsoft-provider': patch
+'@backstage/plugin-auth-backend-module-oidc-provider': patch
+'@backstage/plugin-auth-backend-module-okta-provider': patch
+'@backstage/plugin-auth-backend-module-onelogin-provider': patch
+'@backstage/plugin-auth-backend-module-openshift-provider': patch
+'@backstage/plugin-auth-backend-module-pinniped-provider': patch
+'@backstage/plugin-auth-backend': patch
+'@backstage/plugin-auth-node': patch
+'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch
+'@backstage/plugin-catalog-backend': patch
+'@backstage/plugin-devtools-backend': patch
+'@backstage/plugin-events-backend': patch
+'@backstage/plugin-events-node': patch
+'@backstage/plugin-gateway-backend': patch
+'@backstage/plugin-kubernetes-backend': patch
+'@backstage/plugin-mcp-actions-backend': patch
+'@backstage/plugin-notifications-backend': patch
+'@backstage/plugin-permission-backend': patch
+'@backstage/plugin-permission-node': patch
+'@backstage/plugin-proxy-backend': patch
+'@backstage/plugin-scaffolder-backend': patch
+'@backstage/plugin-search-backend': patch
+'@backstage/plugin-signals-backend': patch
+'@backstage/plugin-signals-node': patch
+'@backstage/plugin-techdocs-backend': patch
+'@backstage/plugin-techdocs-node': patch
+'@backstage/plugin-user-settings-backend': patch
+---
+
+chore(deps): bump `express` from 4.21.2 to 4.22.0
diff --git a/.changeset/dependabot-5e654fe.md b/.changeset/dependabot-5e654fe.md
new file mode 100644
index 0000000000..93563cffc6
--- /dev/null
+++ b/.changeset/dependabot-5e654fe.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-defaults': patch
+---
+
+build(deps): bump `node-forge` from 1.3.1 to 1.3.2
diff --git a/.changeset/early-doors-visit.md b/.changeset/early-doors-visit.md
new file mode 100644
index 0000000000..41cd9e92b8
--- /dev/null
+++ b/.changeset/early-doors-visit.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-defaults': patch
+---
+
+Allow configuration of the `referrerPolicy`
diff --git a/.changeset/error-boundary-api.md b/.changeset/error-boundary-api.md
new file mode 100644
index 0000000000..74803f1dc0
--- /dev/null
+++ b/.changeset/error-boundary-api.md
@@ -0,0 +1,7 @@
+---
+'@backstage/frontend-plugin-api': patch
+---
+
+Added a new `errorPresentation` prop to `ExtensionBoundary` to control how errors are presented to the user. The default is `'error-display'`, which is the current behavior of showing the error in the `ErrorDisplay` component. The new option is `'error-api'`, posts errors to the `ErrorApi` and does not allow retries.
+
+The `AppRootElementBlueprint` now wraps its element in an `ErrorBoundary` using the new `'error-api'` presentation mode.
diff --git a/.changeset/every-ants-count.md b/.changeset/every-ants-count.md
deleted file mode 100644
index 963c3a521a..0000000000
--- a/.changeset/every-ants-count.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/backend-test-utils': minor
----
-
-Added support for Postgres 18 to the available `TestDatabases`.
-
-Note that the set of _default_ databases to test against for users of the `TestDatabases` class was also updated to include Postgres 14 and 18, instead of 13 and 17. If you need to override this, you can pass in an explicit `ids` argument, for example `ids: ['POSTGRES_17', 'POSTGRES_13', 'SQLITE_3']`.
diff --git a/.changeset/every-clocks-arrive.md b/.changeset/every-clocks-arrive.md
deleted file mode 100644
index d2061913e3..0000000000
--- a/.changeset/every-clocks-arrive.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/dev-utils': patch
----
-
-Fixed styling of the dev app by adding a lazy import of `@backstage/ui/css/styles.css`.
diff --git a/.changeset/famous-jars-lose.md b/.changeset/famous-jars-lose.md
new file mode 100644
index 0000000000..4070701e4e
--- /dev/null
+++ b/.changeset/famous-jars-lose.md
@@ -0,0 +1,7 @@
+---
+'@backstage/ui': patch
+---
+
+Fixed Table Row component to correctly handle cases where no `href` is provided, preventing unnecessary router provider wrapping and fixing the cursor incorrectly showing as a pointer despite the element not being a link.
+
+Affected components: Row
diff --git a/.changeset/fifty-coats-feel.md b/.changeset/fifty-coats-feel.md
new file mode 100644
index 0000000000..0e3425a505
--- /dev/null
+++ b/.changeset/fifty-coats-feel.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-kubernetes-react': patch
+---
+
+Enabled a pod terminal at GKE
diff --git a/.changeset/fifty-lights-marry.md b/.changeset/fifty-lights-marry.md
new file mode 100644
index 0000000000..423ef89721
--- /dev/null
+++ b/.changeset/fifty-lights-marry.md
@@ -0,0 +1,9 @@
+---
+'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch
+'@backstage/plugin-kubernetes-backend': patch
+'@backstage/backend-defaults': patch
+'@backstage/plugin-scaffolder-react': patch
+'@backstage/integration': patch
+---
+
+Updated luxon types
diff --git a/.changeset/fine-eagles-sleep.md b/.changeset/fine-eagles-sleep.md
new file mode 100644
index 0000000000..0af6d0dfc0
--- /dev/null
+++ b/.changeset/fine-eagles-sleep.md
@@ -0,0 +1,7 @@
+---
+'@backstage/backend-app-api': minor
+---
+
+Introduced backend startup result tracking and error handling. The `Backend.start()` method now returns a `BackendStartupResult` with detailed success/failure status and timing information for all plugins and modules. When startup fails, a `BackendStartupError` is thrown that includes the complete startup results, making it easier to diagnose which plugins or modules failed.
+
+This also improves the default error message when backend startup fails, and of course makes it possible to craft your own custom error reporting based on the startup results.
diff --git a/.changeset/fine-hands-return.md b/.changeset/fine-hands-return.md
deleted file mode 100644
index d3bf69c9b9..0000000000
--- a/.changeset/fine-hands-return.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/ui': patch
----
-
-Fix default text color in Backstage UI
diff --git a/.changeset/five-seas-jam.md b/.changeset/five-seas-jam.md
deleted file mode 100644
index 4aa481422f..0000000000
--- a/.changeset/five-seas-jam.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-notifications-backend-module-email': patch
----
-
-Updated `AWS SES` client to version 2 to support `nodemailer` version 7.
diff --git a/.changeset/flat-pillows-rush.md b/.changeset/flat-pillows-rush.md
new file mode 100644
index 0000000000..bb64013f62
--- /dev/null
+++ b/.changeset/flat-pillows-rush.md
@@ -0,0 +1,5 @@
+---
+'@backstage/frontend-plugin-api': patch
+---
+
+Fixed a versioning conflict that could result in a `.withContext` is not a function error.
diff --git a/.changeset/floppy-bobcats-serve.md b/.changeset/floppy-bobcats-serve.md
new file mode 100644
index 0000000000..d698e1482c
--- /dev/null
+++ b/.changeset/floppy-bobcats-serve.md
@@ -0,0 +1,12 @@
+---
+'@backstage/core-components': patch
+---
+
+Fixed bug in the `LogViewer` component where shift + click always opened a new window instead of just changing the selection.
+
+In addition, improved the `LogViewer` component by a few usability enhancements:
+
+- Added support for multiple selections using cmd/ctrl + click
+- Improved the generated hash that is added to the URL to also support ranges & multiple selections
+- Added an hover effect & info tooltip to the "Copy to clipboard" button to indicate its functionality
+- Added some color and a separator to the line numbers to improve readability
diff --git a/.changeset/four-peaches-train.md b/.changeset/four-peaches-train.md
new file mode 100644
index 0000000000..81b35589c1
--- /dev/null
+++ b/.changeset/four-peaches-train.md
@@ -0,0 +1,6 @@
+---
+'@backstage/backend-defaults': patch
+'@backstage/backend-plugin-api': patch
+---
+
+**BREAKING ALPHA**: The old `instanceMetadataService` has been removed from alpha. Please switch over to using the stable `coreServices.rootInstanceMetadata` and related types instead, available from `@backstage/backend-plugin-api`.
diff --git a/.changeset/fruity-rivers-arrive.md b/.changeset/fruity-rivers-arrive.md
new file mode 100644
index 0000000000..a816946bcc
--- /dev/null
+++ b/.changeset/fruity-rivers-arrive.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder-backend': minor
+---
+
+Add `defaultEnvironment` config to scaffolder to enable more flexible and custom templates. Now it's possible enable access to default parameters and secrets in templates, improving security and reducing complexity.
diff --git a/.changeset/fruity-words-melt.md b/.changeset/fruity-words-melt.md
new file mode 100644
index 0000000000..0f02acee2b
--- /dev/null
+++ b/.changeset/fruity-words-melt.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-techdocs-module-addons-contrib': patch
+---
+
+Updated tests to match test-utils change
diff --git a/.changeset/full-needles-drive.md b/.changeset/full-needles-drive.md
new file mode 100644
index 0000000000..d0d7a59551
--- /dev/null
+++ b/.changeset/full-needles-drive.md
@@ -0,0 +1,6 @@
+---
+'@backstage/plugin-kubernetes-backend': minor
+'@backstage/plugin-kubernetes-node': minor
+---
+
+Add possibility to extends Kubernetes REST API. Add fetcher to parameters for custom objects provider
diff --git a/.changeset/funny-hornets-peel.md b/.changeset/funny-hornets-peel.md
new file mode 100644
index 0000000000..2af6702295
--- /dev/null
+++ b/.changeset/funny-hornets-peel.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder-backend': patch
+---
+
+Updated `isolated-vm` to `6.0.1`
diff --git a/.changeset/funny-papayas-rest.md b/.changeset/funny-papayas-rest.md
new file mode 100644
index 0000000000..95f52966ec
--- /dev/null
+++ b/.changeset/funny-papayas-rest.md
@@ -0,0 +1,6 @@
+---
+'@backstage/repo-tools': patch
+'@backstage/codemods': patch
+---
+
+Updated to use new utilities from `@backstage/cli-common`.
diff --git a/.changeset/fuzzy-phones-own.md b/.changeset/fuzzy-phones-own.md
new file mode 100644
index 0000000000..61725d1f1d
--- /dev/null
+++ b/.changeset/fuzzy-phones-own.md
@@ -0,0 +1,9 @@
+---
+'@backstage/plugin-catalog-backend-module-github-org': patch
+'@backstage/plugin-catalog-backend-module-github': patch
+---
+
+Introduce new configuration option to exclude suspended users from GitHub Enterprise instances.
+
+When it’s set to true, suspended users won’t be returned when querying the organization users for GitHub Enterprise instances.
+Note that this option should be used only against GitHub Enterprise instances, the property does not exist in the github.com GraphQL schema, setting it will cause a schema validation error and the syncing of users will fail.
diff --git a/.changeset/fuzzy-rivers-travel.md b/.changeset/fuzzy-rivers-travel.md
new file mode 100644
index 0000000000..fa2c08a671
--- /dev/null
+++ b/.changeset/fuzzy-rivers-travel.md
@@ -0,0 +1,5 @@
+---
+'@backstage/frontend-plugin-api': patch
+---
+
+Made the return type of `.withOverrides` to be simplified.
diff --git a/.changeset/fuzzy-trees-live.md b/.changeset/fuzzy-trees-live.md
new file mode 100644
index 0000000000..84cec56fab
--- /dev/null
+++ b/.changeset/fuzzy-trees-live.md
@@ -0,0 +1,5 @@
+---
+'@backstage/core-components': patch
+---
+
+long words like urls now breaks to new line on warning panels instead of overflowing the container
diff --git a/.changeset/gentle-singers-love.md b/.changeset/gentle-singers-love.md
new file mode 100644
index 0000000000..ed4372ee06
--- /dev/null
+++ b/.changeset/gentle-singers-love.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-app': patch
+---
+
+Support to set `defaultLanguage` and `availableLanguages` for the app language API in the new frontend system
diff --git a/.changeset/gentle-trains-juggle.md b/.changeset/gentle-trains-juggle.md
new file mode 100644
index 0000000000..e8b1397180
--- /dev/null
+++ b/.changeset/gentle-trains-juggle.md
@@ -0,0 +1,6 @@
+---
+'@backstage/plugin-kubernetes-backend': patch
+---
+
+Replace `@aws-sdk/signature-v4` with `@smithy/signature-v4`,
+as stated in the [package documentation](https://www.npmjs.com/package/@aws-sdk/signature-v4?activeTab=readme)
diff --git a/.changeset/great-files-shave.md b/.changeset/great-files-shave.md
new file mode 100644
index 0000000000..271a8e8955
--- /dev/null
+++ b/.changeset/great-files-shave.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog-react': patch
+---
+
+Fixed an issue where `EntityOwnerPicker` failed to filter options when the input text contained uppercase characters.
diff --git a/.changeset/green-lizards-boil.md b/.changeset/green-lizards-boil.md
new file mode 100644
index 0000000000..640699e001
--- /dev/null
+++ b/.changeset/green-lizards-boil.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog-react': patch
+---
+
+Use a versioned context for `useEntityList`, to better work with mixed `@backstage/plugin-catalog-react` versions.
diff --git a/.changeset/grumpy-planes-bet.md b/.changeset/grumpy-planes-bet.md
deleted file mode 100644
index bf7d36be41..0000000000
--- a/.changeset/grumpy-planes-bet.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-react': patch
----
-
-Fixed a bug in the Scaffolder's template parsing in the `useTemplateSchema` hook by removing the title instead of setting it to `undefined`
diff --git a/.changeset/happy-bottles-invite.md b/.changeset/happy-bottles-invite.md
new file mode 100644
index 0000000000..07fc222cad
--- /dev/null
+++ b/.changeset/happy-bottles-invite.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-defaults': minor
+---
+
+Move `better-sqlite3` from dependencies to peer dependencies
diff --git a/.changeset/happy-streets-dress.md b/.changeset/happy-streets-dress.md
new file mode 100644
index 0000000000..46f4d798ed
--- /dev/null
+++ b/.changeset/happy-streets-dress.md
@@ -0,0 +1,5 @@
+---
+'@backstage/create-app': patch
+---
+
+Updated engines to support Node 22 or 24
diff --git a/.changeset/honest-bears-itch.md b/.changeset/honest-bears-itch.md
new file mode 100644
index 0000000000..4c0cd5f47e
--- /dev/null
+++ b/.changeset/honest-bears-itch.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-test-utils': patch
+---
+
+Switched `textextensions` dependency for `text-extensions`.
diff --git a/.changeset/kind-hoops-double.md b/.changeset/kind-hoops-double.md
new file mode 100644
index 0000000000..fd2f9f023f
--- /dev/null
+++ b/.changeset/kind-hoops-double.md
@@ -0,0 +1,5 @@
+---
+'@backstage/integration': patch
+---
+
+Made the github urls case insensitive.
diff --git a/.changeset/large-planes-punch.md b/.changeset/large-planes-punch.md
new file mode 100644
index 0000000000..3216653eb9
--- /dev/null
+++ b/.changeset/large-planes-punch.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-defaults': patch
+---
+
+Fix for `jose` types
diff --git a/.changeset/legal-cloths-spend.md b/.changeset/legal-cloths-spend.md
new file mode 100644
index 0000000000..50d965307a
--- /dev/null
+++ b/.changeset/legal-cloths-spend.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-mcp-actions-backend': patch
+---
+
+Clarify error handling in readme and update handleError.ts to include all backstage/errors
diff --git a/.changeset/legal-otters-punch.md b/.changeset/legal-otters-punch.md
new file mode 100644
index 0000000000..2003492e29
--- /dev/null
+++ b/.changeset/legal-otters-punch.md
@@ -0,0 +1,7 @@
+---
+'@backstage/plugin-kubernetes-backend': patch
+'@backstage/plugin-kubernetes-common': patch
+'@backstage/plugin-kubernetes-react': patch
+---
+
+Add Kubernetes Plugin Secrets Accordion with masked secret datas
diff --git a/.changeset/lemon-corners-hug.md b/.changeset/lemon-corners-hug.md
new file mode 100644
index 0000000000..b41c09c928
--- /dev/null
+++ b/.changeset/lemon-corners-hug.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-techdocs-node': patch
+---
+
+There was an issue in the uploading of large size files to the AWS S3. We have modified the logic by adding retry along with multipart uploading functionality.
diff --git a/.changeset/loose-pets-slide.md b/.changeset/loose-pets-slide.md
new file mode 100644
index 0000000000..b8ec201f1c
--- /dev/null
+++ b/.changeset/loose-pets-slide.md
@@ -0,0 +1,10 @@
+---
+'@backstage/repo-tools': patch
+---
+
+Updates OpenAPI generator templates to preserve original property names (like 'group-name', 'user-id') from OpenAPI specs when propertyNaming=original is specified. Previously, these were always converted to camelCase regardless of the propertyNaming setting.
+
+- Updates modelGeneric.mustache templates in both client and server generators
+- Updates modelTaggedUnion.mustache templates in both client and server generators
+- Uses {{baseName}} when available, falls back to {{name}} for backward compatibility
+- Maintains backward compatibility - no changes when propertyNaming=original is not used
diff --git a/.changeset/loud-carpets-throw.md b/.changeset/loud-carpets-throw.md
deleted file mode 100644
index 169703d815..0000000000
--- a/.changeset/loud-carpets-throw.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-Added a template for the `yarn new` command to create an catalog entity provider. To add this template to an explicit list in the root `package.json`, use `@backstage/cli/templates/catalog-provider-module`.
diff --git a/.changeset/loud-yaks-watch.md b/.changeset/loud-yaks-watch.md
new file mode 100644
index 0000000000..b7ad65b9eb
--- /dev/null
+++ b/.changeset/loud-yaks-watch.md
@@ -0,0 +1,5 @@
+---
+'@backstage/repo-tools': patch
+---
+
+Bump `@microsoft/api-documenter` and `@microsoft/api-extractor` to latest versions.
diff --git a/.changeset/lucky-days-hug.md b/.changeset/lucky-days-hug.md
new file mode 100644
index 0000000000..4d76caa098
--- /dev/null
+++ b/.changeset/lucky-days-hug.md
@@ -0,0 +1,6 @@
+---
+'@backstage/plugin-kubernetes-react': patch
+'@backstage/plugin-kubernetes': patch
+---
+
+add missing i18n support for kubernetes and kubernetes-react
diff --git a/.changeset/many-planes-join.md b/.changeset/many-planes-join.md
new file mode 100644
index 0000000000..66107cd370
--- /dev/null
+++ b/.changeset/many-planes-join.md
@@ -0,0 +1,6 @@
+---
+'@backstage/core-app-api': patch
+'@backstage/test-utils': patch
+---
+
+Internal update of translation imports.
diff --git a/.changeset/metal-boxes-laugh.md b/.changeset/metal-boxes-laugh.md
new file mode 100644
index 0000000000..9a5ab24e0e
--- /dev/null
+++ b/.changeset/metal-boxes-laugh.md
@@ -0,0 +1,5 @@
+---
+'@backstage/core-plugin-api': patch
+---
+
+The `useApp` and `useRouteRef` functions are now forwards compatible with the new frontend system. Along with the previous route reference changes this means that there is no longer a need to use `compatWrapper` from `@backstage/core-compat-api` to make code based on `@backstage/core-plugin-api` compatible with `@backstage/frontend-plugin-api` APIs.
diff --git a/.changeset/metal-humans-lose.md b/.changeset/metal-humans-lose.md
new file mode 100644
index 0000000000..f96d33a779
--- /dev/null
+++ b/.changeset/metal-humans-lose.md
@@ -0,0 +1,21 @@
+---
+'@backstage/plugin-catalog-unprocessed-entities': patch
+'@backstage/plugin-app-visualizer': patch
+'@backstage/plugin-catalog-import': patch
+'@backstage/plugin-catalog-graph': patch
+'@backstage/plugin-catalog-react': patch
+'@backstage/plugin-notifications': patch
+'@backstage/plugin-user-settings': patch
+'@backstage/plugin-kubernetes': patch
+'@backstage/plugin-scaffolder': patch
+'@backstage/plugin-api-docs': patch
+'@backstage/plugin-devtools': patch
+'@backstage/plugin-techdocs': patch
+'@backstage/plugin-catalog': patch
+'@backstage/plugin-search': patch
+'@backstage/plugin-home': patch
+'@backstage/plugin-app': patch
+'@backstage/plugin-org': patch
+---
+
+Remove unnecessary use of `compatWrapper` and `convertLegacyRouteRef`(s) for the new frontend system.
diff --git a/.changeset/modern-taxes-start.md b/.changeset/modern-taxes-start.md
new file mode 100644
index 0000000000..9fd7645570
--- /dev/null
+++ b/.changeset/modern-taxes-start.md
@@ -0,0 +1,5 @@
+---
+'@backstage/cli-common': patch
+---
+
+Added new `run`, `runOutput`, and `runCheck` utilities to help run child processes in a safe and portable way.
diff --git a/.changeset/neat-pens-clean.md b/.changeset/neat-pens-clean.md
new file mode 100644
index 0000000000..aff67a2a10
--- /dev/null
+++ b/.changeset/neat-pens-clean.md
@@ -0,0 +1,5 @@
+---
+'@backstage/repo-tools': patch
+---
+
+Properly create workspace in OS temporary directory for `generate-patch` command
diff --git a/.changeset/nice-humans-cry.md b/.changeset/nice-humans-cry.md
new file mode 100644
index 0000000000..c6119702b4
--- /dev/null
+++ b/.changeset/nice-humans-cry.md
@@ -0,0 +1,6 @@
+---
+'@backstage/plugin-scaffolder-backend': patch
+'@backstage/backend-defaults': patch
+---
+
+Internal change to support new versions of the `logform` library
diff --git a/.changeset/nice-trams-shake.md b/.changeset/nice-trams-shake.md
new file mode 100644
index 0000000000..b58c03200f
--- /dev/null
+++ b/.changeset/nice-trams-shake.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder-react': patch
+---
+
+Fixed bug where custom `review.name` values were incorrectly formatted by `startCase`, preserving them exactly as written.
diff --git a/.changeset/ninety-cobras-feel.md b/.changeset/ninety-cobras-feel.md
deleted file mode 100644
index e287ef33ed..0000000000
--- a/.changeset/ninety-cobras-feel.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-techdocs': patch
----
-
-Fixed an issue where the entire TechDocs page would re-render when navigating between pages within the same entity's documentation.
diff --git a/.changeset/old-cats-shake.md b/.changeset/old-cats-shake.md
new file mode 100644
index 0000000000..3ca30a0421
--- /dev/null
+++ b/.changeset/old-cats-shake.md
@@ -0,0 +1,6 @@
+---
+'@backstage/backend-defaults': patch
+'@backstage/backend-plugin-api': minor
+---
+
+Adds a new experimental `RootSystemMetadataService` for tracking the collection of Backstage instances that may be deployed at any one time. It currently offers a single API, `getInstalledPlugins` that returns a list of installed plugins based on config you have set up in `discovery.endpoints` as well as the plugins installed on the instance you're calling the API with. It does not handle wildcard values or fallback values. The intention is for this plugin to provide plugin authors with a simple interface to fetch a trustworthy list of all installed plugins.
diff --git a/.changeset/old-parks-smell.md b/.changeset/old-parks-smell.md
new file mode 100644
index 0000000000..f089c7da21
--- /dev/null
+++ b/.changeset/old-parks-smell.md
@@ -0,0 +1,20 @@
+---
+'@backstage/plugin-techdocs-addons-test-utils': major
+---
+
+**BREAKING**: `TechDocsAddonTester.renderWithEffects()` no longer returns a screen; this means that you can no longer grab assertions such as `getByText` from its return value.
+
+Newer versions of `@testing-library` recommends using the `screen` export for assertions - and removing this from the addon tester contract allows us to more freely iterate on which underlying version of the testing library is being used.
+
+One notable effect of this, however, is that the `@testing-library` `screen` does NOT support assertions on the shadow DOM, which techdocs relies on. You will therefore want to add a dependency on [the `shadow-dom-testing-library` package](https://github.com/konnorrogers/shadow-dom-testing-library/) in your tests, and using its `screen` and its dedicated `*Shadow*` methods. As an example, if you keep doing `getByText` you will not get matches inside the shadow DOM - switch to `getByShadowText` instead.
+
+```ts
+import { screen } from 'shadow-dom-testing-library';
+
+// ... render the addon ...
+await TechDocsAddonTester.buildAddonsInTechDocs([])
+ .withDom(
TEST_CONTENT)
+ .renderWithEffects();
+
+expect(screen.getByShadowText('TEST_CONTENT')).toBeInTheDocument();
+```
diff --git a/.changeset/open-points-beam.md b/.changeset/open-points-beam.md
new file mode 100644
index 0000000000..b58bf2b3d5
--- /dev/null
+++ b/.changeset/open-points-beam.md
@@ -0,0 +1,37 @@
+---
+'@backstage/ui': minor
+---
+
+**Breaking change** The `Cell` component has been refactored to be a generic wrapper component that accepts `children` for custom cell content. The text-specific functionality (previously part of `Cell`) has been moved to a new `CellText` component.
+
+### Migration Guide
+
+If you were using `Cell` with text-specific props (`title`, `description`, `leadingIcon`, `href`), you need to update your code to use `CellText` instead:
+
+**Before:**
+
+```tsx
+}
+ href="/path"
+/>
+```
+
+**After:**
+
+```tsx
+}
+ href="/path"
+/>
+```
+
+For custom cell content, use the new generic `Cell` component:
+
+```tsx
+{/* Your custom content */}
+```
diff --git a/.changeset/polite-seas-divide.md b/.changeset/polite-seas-divide.md
deleted file mode 100644
index e265e6df90..0000000000
--- a/.changeset/polite-seas-divide.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/ui': patch
----
-
-Fix the default font size in Backstage UI.
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 67e48bb29c..f44b869497 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -2,230 +2,256 @@
"mode": "pre",
"tag": "next",
"initialVersions": {
- "example-app": "0.2.114",
- "@backstage/app-defaults": "1.7.1",
- "example-app-next": "0.0.28",
- "app-next-example-plugin": "0.0.28",
- "example-backend": "0.0.43",
- "@backstage/backend-app-api": "1.2.8",
- "@backstage/backend-defaults": "0.13.0",
+ "example-app": "0.2.115",
+ "@backstage/app-defaults": "1.7.2",
+ "example-app-next": "0.0.29",
+ "app-next-example-plugin": "0.0.29",
+ "example-backend": "0.0.44",
+ "@backstage/backend-app-api": "1.3.0",
+ "@backstage/backend-defaults": "0.13.1",
"@backstage/backend-dev-utils": "0.1.5",
- "@backstage/backend-dynamic-feature-service": "0.7.5",
- "@backstage/backend-openapi-utils": "0.6.2",
- "@backstage/backend-plugin-api": "1.4.4",
- "@backstage/backend-test-utils": "1.9.1",
- "@backstage/catalog-client": "1.12.0",
- "@backstage/catalog-model": "1.7.5",
- "@backstage/cli": "0.34.4",
+ "@backstage/backend-dynamic-feature-service": "0.7.6",
+ "@backstage/backend-openapi-utils": "0.6.3",
+ "@backstage/backend-plugin-api": "1.5.0",
+ "@backstage/backend-test-utils": "1.10.0",
+ "@backstage/catalog-client": "1.12.1",
+ "@backstage/catalog-model": "1.7.6",
+ "@backstage/cli": "0.34.5",
"@backstage/cli-common": "0.1.15",
- "@backstage/cli-node": "0.2.14",
+ "@backstage/cli-node": "0.2.15",
"@backstage/codemods": "0.1.52",
- "@backstage/config": "1.3.5",
- "@backstage/config-loader": "1.10.5",
- "@backstage/core-app-api": "1.19.1",
- "@backstage/core-compat-api": "0.5.3",
- "@backstage/core-components": "0.18.2",
- "@backstage/core-plugin-api": "1.11.1",
- "@backstage/create-app": "0.7.5",
- "@backstage/dev-utils": "1.1.15",
- "e2e-test": "0.2.33",
+ "@backstage/config": "1.3.6",
+ "@backstage/config-loader": "1.10.6",
+ "@backstage/core-app-api": "1.19.2",
+ "@backstage/core-compat-api": "0.5.4",
+ "@backstage/core-components": "0.18.3",
+ "@backstage/core-plugin-api": "1.12.0",
+ "@backstage/create-app": "0.7.6",
+ "@backstage/dev-utils": "1.1.17",
+ "e2e-test": "0.2.34",
"@backstage/e2e-test-utils": "0.1.1",
"@backstage/errors": "1.2.7",
- "@backstage/eslint-plugin": "0.1.12",
- "@backstage/frontend-app-api": "0.13.1",
- "@backstage/frontend-defaults": "0.3.2",
- "@backstage/frontend-dynamic-feature-loader": "0.1.6",
- "@internal/frontend": "0.0.14",
- "@backstage/frontend-plugin-api": "0.12.1",
- "@backstage/frontend-test-utils": "0.4.0",
- "@backstage/integration": "1.18.1",
- "@backstage/integration-aws-node": "0.1.18",
- "@backstage/integration-react": "1.2.11",
+ "@backstage/eslint-plugin": "0.2.0",
+ "@backstage/frontend-app-api": "0.13.2",
+ "@backstage/frontend-defaults": "0.3.3",
+ "@backstage/frontend-dynamic-feature-loader": "0.1.7",
+ "@internal/frontend": "0.0.15",
+ "@backstage/frontend-plugin-api": "0.13.0",
+ "@backstage/frontend-test-utils": "0.4.1",
+ "@backstage/integration": "1.18.2",
+ "@backstage/integration-aws-node": "0.1.19",
+ "@backstage/integration-react": "1.2.12",
"@internal/opaque": "0.0.1",
"@backstage/release-manifests": "0.0.13",
- "@backstage/repo-tools": "0.15.3",
- "@internal/scaffolder": "0.0.14",
- "@techdocs/cli": "1.10.0",
- "techdocs-cli-embedded-app": "0.2.113",
- "@backstage/test-utils": "1.7.12",
+ "@backstage/repo-tools": "0.16.0",
+ "@internal/scaffolder": "0.0.15",
+ "@techdocs/cli": "1.10.2",
+ "techdocs-cli-embedded-app": "0.2.114",
+ "@backstage/test-utils": "1.7.13",
"@backstage/theme": "0.7.0",
"@backstage/types": "1.2.2",
- "@backstage/ui": "0.8.0",
+ "@backstage/ui": "0.9.0",
"@backstage/version-bridge": "1.0.11",
"yarn-plugin-backstage": "0.0.7",
- "@backstage/plugin-api-docs": "0.13.0",
+ "@backstage/plugin-api-docs": "0.13.1",
"@backstage/plugin-api-docs-module-protoc-gen-doc": "0.1.10",
- "@backstage/plugin-app": "0.3.1",
- "@backstage/plugin-app-backend": "0.5.7",
- "@backstage/plugin-app-node": "0.1.38",
- "@backstage/plugin-app-visualizer": "0.1.24",
- "@backstage/plugin-auth": "0.1.1",
- "@backstage/plugin-auth-backend": "0.25.5",
- "@backstage/plugin-auth-backend-module-atlassian-provider": "0.4.8",
- "@backstage/plugin-auth-backend-module-auth0-provider": "0.2.8",
- "@backstage/plugin-auth-backend-module-aws-alb-provider": "0.4.8",
- "@backstage/plugin-auth-backend-module-azure-easyauth-provider": "0.2.13",
- "@backstage/plugin-auth-backend-module-bitbucket-provider": "0.3.8",
- "@backstage/plugin-auth-backend-module-bitbucket-server-provider": "0.2.8",
- "@backstage/plugin-auth-backend-module-cloudflare-access-provider": "0.4.8",
- "@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.4.8",
- "@backstage/plugin-auth-backend-module-github-provider": "0.3.8",
- "@backstage/plugin-auth-backend-module-gitlab-provider": "0.3.8",
- "@backstage/plugin-auth-backend-module-google-provider": "0.3.8",
- "@backstage/plugin-auth-backend-module-guest-provider": "0.2.13",
- "@backstage/plugin-auth-backend-module-microsoft-provider": "0.3.8",
- "@backstage/plugin-auth-backend-module-oauth2-provider": "0.4.8",
- "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.2.13",
- "@backstage/plugin-auth-backend-module-oidc-provider": "0.4.8",
- "@backstage/plugin-auth-backend-module-okta-provider": "0.2.8",
- "@backstage/plugin-auth-backend-module-onelogin-provider": "0.3.8",
- "@backstage/plugin-auth-backend-module-openshift-provider": "0.1.1",
- "@backstage/plugin-auth-backend-module-pinniped-provider": "0.3.8",
- "@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.5.8",
- "@backstage/plugin-auth-node": "0.6.8",
- "@backstage/plugin-auth-react": "0.1.20",
- "@backstage/plugin-bitbucket-cloud-common": "0.3.3",
- "@backstage/plugin-catalog": "1.31.4",
- "@backstage/plugin-catalog-backend": "3.1.2",
- "@backstage/plugin-catalog-backend-module-aws": "0.4.16",
- "@backstage/plugin-catalog-backend-module-azure": "0.3.10",
- "@backstage/plugin-catalog-backend-module-backstage-openapi": "0.5.7",
- "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.5.4",
- "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.5.4",
- "@backstage/plugin-catalog-backend-module-gcp": "0.3.13",
- "@backstage/plugin-catalog-backend-module-gerrit": "0.3.7",
- "@backstage/plugin-catalog-backend-module-gitea": "0.1.5",
- "@backstage/plugin-catalog-backend-module-github": "0.11.1",
- "@backstage/plugin-catalog-backend-module-github-org": "0.3.15",
- "@backstage/plugin-catalog-backend-module-gitlab": "0.7.4",
- "@backstage/plugin-catalog-backend-module-gitlab-org": "0.2.14",
- "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.7.5",
- "@backstage/plugin-catalog-backend-module-ldap": "0.11.10",
- "@backstage/plugin-catalog-backend-module-logs": "0.1.15",
- "@backstage/plugin-catalog-backend-module-msgraph": "0.8.1",
- "@backstage/plugin-catalog-backend-module-openapi": "0.2.15",
- "@backstage/plugin-catalog-backend-module-puppetdb": "0.2.15",
- "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.2.13",
- "@backstage/plugin-catalog-backend-module-unprocessed": "0.6.5",
- "@backstage/plugin-catalog-common": "1.1.6",
- "@backstage/plugin-catalog-graph": "0.5.2",
- "@backstage/plugin-catalog-import": "0.13.6",
- "@backstage/plugin-catalog-node": "1.19.1",
- "@backstage/plugin-catalog-react": "1.21.2",
- "@backstage/plugin-catalog-unprocessed-entities": "0.2.22",
- "@backstage/plugin-catalog-unprocessed-entities-common": "0.0.10",
- "@backstage/plugin-config-schema": "0.1.73",
- "@backstage/plugin-devtools": "0.1.32",
- "@backstage/plugin-devtools-backend": "0.5.10",
- "@backstage/plugin-devtools-common": "0.1.18",
- "@backstage/plugin-events-backend": "0.5.7",
- "@backstage/plugin-events-backend-module-aws-sqs": "0.4.16",
- "@backstage/plugin-events-backend-module-azure": "0.2.25",
- "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.2.25",
- "@backstage/plugin-events-backend-module-bitbucket-server": "0.1.6",
- "@backstage/plugin-events-backend-module-gerrit": "0.2.25",
- "@backstage/plugin-events-backend-module-github": "0.4.5",
- "@backstage/plugin-events-backend-module-gitlab": "0.3.6",
- "@backstage/plugin-events-backend-module-google-pubsub": "0.1.5",
- "@backstage/plugin-events-backend-module-kafka": "0.1.4",
- "@backstage/plugin-events-backend-test-utils": "0.1.49",
- "@backstage/plugin-events-node": "0.4.16",
- "@internal/plugin-todo-list": "1.0.44",
- "@internal/plugin-todo-list-backend": "1.0.44",
- "@internal/plugin-todo-list-common": "1.0.27",
- "@backstage/plugin-gateway-backend": "1.0.6",
- "@backstage/plugin-home": "0.8.13",
- "@backstage/plugin-home-react": "0.1.31",
- "@backstage/plugin-kubernetes": "0.12.12",
- "@backstage/plugin-kubernetes-backend": "0.20.3",
- "@backstage/plugin-kubernetes-cluster": "0.0.30",
- "@backstage/plugin-kubernetes-common": "0.9.7",
- "@backstage/plugin-kubernetes-node": "0.3.5",
- "@backstage/plugin-kubernetes-react": "0.5.12",
- "@backstage/plugin-mcp-actions-backend": "0.1.4",
- "@backstage/plugin-mui-to-bui": "0.2.0",
- "@backstage/plugin-notifications": "0.5.10",
- "@backstage/plugin-notifications-backend": "0.5.11",
- "@backstage/plugin-notifications-backend-module-email": "0.3.14",
- "@backstage/plugin-notifications-backend-module-slack": "0.2.0",
- "@backstage/plugin-notifications-common": "0.1.1",
- "@backstage/plugin-notifications-node": "0.2.20",
- "@backstage/plugin-org": "0.6.45",
- "@backstage/plugin-org-react": "0.1.43",
- "@backstage/plugin-permission-backend": "0.7.5",
- "@backstage/plugin-permission-backend-module-allow-all-policy": "0.2.13",
- "@backstage/plugin-permission-common": "0.9.2",
- "@backstage/plugin-permission-node": "0.10.5",
- "@backstage/plugin-permission-react": "0.4.37",
- "@backstage/plugin-proxy-backend": "0.6.7",
- "@backstage/plugin-proxy-node": "0.1.9",
- "@backstage/plugin-scaffolder": "1.34.2",
- "@backstage/plugin-scaffolder-backend": "3.0.0",
- "@backstage/plugin-scaffolder-backend-module-azure": "0.2.14",
- "@backstage/plugin-scaffolder-backend-module-bitbucket": "0.3.15",
- "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "0.2.14",
- "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "0.2.14",
- "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.3.14",
- "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.3.16",
- "@backstage/plugin-scaffolder-backend-module-gcp": "0.2.14",
- "@backstage/plugin-scaffolder-backend-module-gerrit": "0.2.14",
- "@backstage/plugin-scaffolder-backend-module-gitea": "0.2.14",
- "@backstage/plugin-scaffolder-backend-module-github": "0.9.1",
- "@backstage/plugin-scaffolder-backend-module-gitlab": "0.9.6",
- "@backstage/plugin-scaffolder-backend-module-notifications": "0.1.15",
- "@backstage/plugin-scaffolder-backend-module-rails": "0.5.14",
- "@backstage/plugin-scaffolder-backend-module-sentry": "0.2.14",
- "@backstage/plugin-scaffolder-backend-module-yeoman": "0.4.15",
- "@backstage/plugin-scaffolder-common": "1.7.2",
- "@backstage/plugin-scaffolder-node": "0.12.0",
- "@backstage/plugin-scaffolder-node-test-utils": "0.3.4",
- "@backstage/plugin-scaffolder-react": "1.19.2",
- "@backstage/plugin-search": "1.4.31",
- "@backstage/plugin-search-backend": "2.0.7",
- "@backstage/plugin-search-backend-module-catalog": "0.3.9",
- "@backstage/plugin-search-backend-module-elasticsearch": "1.7.7",
- "@backstage/plugin-search-backend-module-explore": "0.3.8",
- "@backstage/plugin-search-backend-module-pg": "0.5.49",
- "@backstage/plugin-search-backend-module-stack-overflow-collator": "0.3.14",
- "@backstage/plugin-search-backend-module-techdocs": "0.4.7",
- "@backstage/plugin-search-backend-node": "1.3.16",
- "@backstage/plugin-search-common": "1.2.20",
- "@backstage/plugin-search-react": "1.9.5",
- "@backstage/plugin-signals": "0.0.24",
- "@backstage/plugin-signals-backend": "0.3.9",
- "@backstage/plugin-signals-node": "0.1.25",
- "@backstage/plugin-signals-react": "0.0.16",
- "@backstage/plugin-techdocs": "1.15.1",
- "@backstage/plugin-techdocs-addons-test-utils": "1.1.1",
- "@backstage/plugin-techdocs-backend": "2.1.1",
+ "@backstage/plugin-app": "0.3.2",
+ "@backstage/plugin-app-backend": "0.5.8",
+ "@backstage/plugin-app-node": "0.1.39",
+ "@backstage/plugin-app-visualizer": "0.1.25",
+ "@backstage/plugin-auth": "0.1.2",
+ "@backstage/plugin-auth-backend": "0.25.6",
+ "@backstage/plugin-auth-backend-module-atlassian-provider": "0.4.9",
+ "@backstage/plugin-auth-backend-module-auth0-provider": "0.2.9",
+ "@backstage/plugin-auth-backend-module-aws-alb-provider": "0.4.9",
+ "@backstage/plugin-auth-backend-module-azure-easyauth-provider": "0.2.14",
+ "@backstage/plugin-auth-backend-module-bitbucket-provider": "0.3.9",
+ "@backstage/plugin-auth-backend-module-bitbucket-server-provider": "0.2.9",
+ "@backstage/plugin-auth-backend-module-cloudflare-access-provider": "0.4.9",
+ "@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.4.9",
+ "@backstage/plugin-auth-backend-module-github-provider": "0.3.9",
+ "@backstage/plugin-auth-backend-module-gitlab-provider": "0.3.9",
+ "@backstage/plugin-auth-backend-module-google-provider": "0.3.9",
+ "@backstage/plugin-auth-backend-module-guest-provider": "0.2.14",
+ "@backstage/plugin-auth-backend-module-microsoft-provider": "0.3.9",
+ "@backstage/plugin-auth-backend-module-oauth2-provider": "0.4.9",
+ "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.2.14",
+ "@backstage/plugin-auth-backend-module-oidc-provider": "0.4.9",
+ "@backstage/plugin-auth-backend-module-okta-provider": "0.2.9",
+ "@backstage/plugin-auth-backend-module-onelogin-provider": "0.3.9",
+ "@backstage/plugin-auth-backend-module-openshift-provider": "0.1.2",
+ "@backstage/plugin-auth-backend-module-pinniped-provider": "0.3.9",
+ "@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.5.9",
+ "@backstage/plugin-auth-node": "0.6.9",
+ "@backstage/plugin-auth-react": "0.1.21",
+ "@backstage/plugin-bitbucket-cloud-common": "0.3.4",
+ "@backstage/plugin-catalog": "1.32.0",
+ "@backstage/plugin-catalog-backend": "3.2.0",
+ "@backstage/plugin-catalog-backend-module-aws": "0.4.17",
+ "@backstage/plugin-catalog-backend-module-azure": "0.3.11",
+ "@backstage/plugin-catalog-backend-module-backstage-openapi": "0.5.8",
+ "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.5.5",
+ "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.5.5",
+ "@backstage/plugin-catalog-backend-module-gcp": "0.3.14",
+ "@backstage/plugin-catalog-backend-module-gerrit": "0.3.8",
+ "@backstage/plugin-catalog-backend-module-gitea": "0.1.6",
+ "@backstage/plugin-catalog-backend-module-github": "0.11.2",
+ "@backstage/plugin-catalog-backend-module-github-org": "0.3.16",
+ "@backstage/plugin-catalog-backend-module-gitlab": "0.7.5",
+ "@backstage/plugin-catalog-backend-module-gitlab-org": "0.2.15",
+ "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.7.6",
+ "@backstage/plugin-catalog-backend-module-ldap": "0.12.0",
+ "@backstage/plugin-catalog-backend-module-logs": "0.1.16",
+ "@backstage/plugin-catalog-backend-module-msgraph": "0.8.2",
+ "@backstage/plugin-catalog-backend-module-openapi": "0.2.16",
+ "@backstage/plugin-catalog-backend-module-puppetdb": "0.2.16",
+ "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.2.14",
+ "@backstage/plugin-catalog-backend-module-unprocessed": "0.6.6",
+ "@backstage/plugin-catalog-common": "1.1.7",
+ "@backstage/plugin-catalog-graph": "0.5.3",
+ "@backstage/plugin-catalog-import": "0.13.7",
+ "@backstage/plugin-catalog-node": "1.20.0",
+ "@backstage/plugin-catalog-react": "1.21.3",
+ "@backstage/plugin-catalog-unprocessed-entities": "0.2.23",
+ "@backstage/plugin-catalog-unprocessed-entities-common": "0.0.11",
+ "@backstage/plugin-config-schema": "0.1.74",
+ "@backstage/plugin-devtools": "0.1.33",
+ "@backstage/plugin-devtools-backend": "0.5.11",
+ "@backstage/plugin-devtools-common": "0.1.19",
+ "@backstage/plugin-events-backend": "0.5.8",
+ "@backstage/plugin-events-backend-module-aws-sqs": "0.4.17",
+ "@backstage/plugin-events-backend-module-azure": "0.2.26",
+ "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.2.26",
+ "@backstage/plugin-events-backend-module-bitbucket-server": "0.1.7",
+ "@backstage/plugin-events-backend-module-gerrit": "0.2.26",
+ "@backstage/plugin-events-backend-module-github": "0.4.6",
+ "@backstage/plugin-events-backend-module-gitlab": "0.3.7",
+ "@backstage/plugin-events-backend-module-google-pubsub": "0.1.6",
+ "@backstage/plugin-events-backend-module-kafka": "0.1.5",
+ "@backstage/plugin-events-backend-test-utils": "0.1.50",
+ "@backstage/plugin-events-node": "0.4.17",
+ "@internal/plugin-todo-list": "1.0.45",
+ "@internal/plugin-todo-list-backend": "1.0.45",
+ "@internal/plugin-todo-list-common": "1.0.28",
+ "@backstage/plugin-gateway-backend": "1.1.0",
+ "@backstage/plugin-home": "0.8.14",
+ "@backstage/plugin-home-react": "0.1.32",
+ "@backstage/plugin-kubernetes": "0.12.13",
+ "@backstage/plugin-kubernetes-backend": "0.20.4",
+ "@backstage/plugin-kubernetes-cluster": "0.0.31",
+ "@backstage/plugin-kubernetes-common": "0.9.8",
+ "@backstage/plugin-kubernetes-node": "0.3.6",
+ "@backstage/plugin-kubernetes-react": "0.5.13",
+ "@backstage/plugin-mcp-actions-backend": "0.1.5",
+ "@backstage/plugin-mui-to-bui": "0.2.1",
+ "@backstage/plugin-notifications": "0.5.11",
+ "@backstage/plugin-notifications-backend": "0.6.0",
+ "@backstage/plugin-notifications-backend-module-email": "0.3.16",
+ "@backstage/plugin-notifications-backend-module-slack": "0.2.1",
+ "@backstage/plugin-notifications-common": "0.2.0",
+ "@backstage/plugin-notifications-node": "0.2.21",
+ "@backstage/plugin-org": "0.6.46",
+ "@backstage/plugin-org-react": "0.1.44",
+ "@backstage/plugin-permission-backend": "0.7.6",
+ "@backstage/plugin-permission-backend-module-allow-all-policy": "0.2.14",
+ "@backstage/plugin-permission-common": "0.9.3",
+ "@backstage/plugin-permission-node": "0.10.6",
+ "@backstage/plugin-permission-react": "0.4.38",
+ "@backstage/plugin-proxy-backend": "0.6.8",
+ "@backstage/plugin-proxy-node": "0.1.10",
+ "@backstage/plugin-scaffolder": "1.34.3",
+ "@backstage/plugin-scaffolder-backend": "3.0.1",
+ "@backstage/plugin-scaffolder-backend-module-azure": "0.2.15",
+ "@backstage/plugin-scaffolder-backend-module-bitbucket": "0.3.16",
+ "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "0.2.15",
+ "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "0.2.15",
+ "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.3.15",
+ "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.3.17",
+ "@backstage/plugin-scaffolder-backend-module-gcp": "0.2.15",
+ "@backstage/plugin-scaffolder-backend-module-gerrit": "0.2.15",
+ "@backstage/plugin-scaffolder-backend-module-gitea": "0.2.15",
+ "@backstage/plugin-scaffolder-backend-module-github": "0.9.2",
+ "@backstage/plugin-scaffolder-backend-module-gitlab": "0.10.0",
+ "@backstage/plugin-scaffolder-backend-module-notifications": "0.1.16",
+ "@backstage/plugin-scaffolder-backend-module-rails": "0.5.15",
+ "@backstage/plugin-scaffolder-backend-module-sentry": "0.2.15",
+ "@backstage/plugin-scaffolder-backend-module-yeoman": "0.4.16",
+ "@backstage/plugin-scaffolder-common": "1.7.3",
+ "@backstage/plugin-scaffolder-node": "0.12.1",
+ "@backstage/plugin-scaffolder-node-test-utils": "0.3.5",
+ "@backstage/plugin-scaffolder-react": "1.19.3",
+ "@backstage/plugin-search": "1.5.0",
+ "@backstage/plugin-search-backend": "2.0.8",
+ "@backstage/plugin-search-backend-module-catalog": "0.3.10",
+ "@backstage/plugin-search-backend-module-elasticsearch": "1.7.8",
+ "@backstage/plugin-search-backend-module-explore": "0.3.9",
+ "@backstage/plugin-search-backend-module-pg": "0.5.50",
+ "@backstage/plugin-search-backend-module-stack-overflow-collator": "0.3.15",
+ "@backstage/plugin-search-backend-module-techdocs": "0.4.8",
+ "@backstage/plugin-search-backend-node": "1.3.17",
+ "@backstage/plugin-search-common": "1.2.21",
+ "@backstage/plugin-search-react": "1.10.0",
+ "@backstage/plugin-signals": "0.0.25",
+ "@backstage/plugin-signals-backend": "0.3.10",
+ "@backstage/plugin-signals-node": "0.1.26",
+ "@backstage/plugin-signals-react": "0.0.17",
+ "@backstage/plugin-techdocs": "1.16.0",
+ "@backstage/plugin-techdocs-addons-test-utils": "1.1.2",
+ "@backstage/plugin-techdocs-backend": "2.1.2",
"@backstage/plugin-techdocs-common": "0.1.1",
- "@backstage/plugin-techdocs-module-addons-contrib": "1.1.29",
- "@backstage/plugin-techdocs-node": "1.13.8",
- "@backstage/plugin-techdocs-react": "1.3.4",
- "@backstage/plugin-user-settings": "0.8.27",
- "@backstage/plugin-user-settings-backend": "0.3.7",
+ "@backstage/plugin-techdocs-module-addons-contrib": "1.1.30",
+ "@backstage/plugin-techdocs-node": "1.13.9",
+ "@backstage/plugin-techdocs-react": "1.3.5",
+ "@backstage/plugin-user-settings": "0.8.29",
+ "@backstage/plugin-user-settings-backend": "0.3.8",
"@backstage/plugin-user-settings-common": "0.0.1"
},
"changesets": [
- "better-hats-cross",
- "better-steaks-act",
- "every-ants-count",
- "every-clocks-arrive",
- "fine-hands-return",
- "five-seas-jam",
- "grumpy-planes-bet",
- "loud-carpets-throw",
- "ninety-cobras-feel",
- "polite-seas-divide",
- "rich-streets-rule",
- "short-sides-feel",
- "silver-garlics-thank",
- "solid-bees-agree",
- "solid-dancers-march",
- "stupid-doodles-love",
- "tender-regions-know",
- "typescript-constructor-refactor",
- "warm-moments-repeat",
- "wild-owls-divide"
+ "afraid-items-drum",
+ "bumpy-planets-go",
+ "chatty-sides-wear",
+ "chilly-bikes-rule",
+ "chilly-hotels-walk",
+ "clean-toys-reply",
+ "common-coins-stare",
+ "create-app-1764689798",
+ "famous-jars-lose",
+ "fifty-coats-feel",
+ "fine-eagles-sleep",
+ "flat-pillows-rush",
+ "floppy-bobcats-serve",
+ "four-peaches-train",
+ "fruity-rivers-arrive",
+ "fruity-words-melt",
+ "funny-papayas-rest",
+ "fuzzy-phones-own",
+ "fuzzy-trees-live",
+ "gentle-trains-juggle",
+ "great-files-shave",
+ "happy-bottles-invite",
+ "kind-hoops-double",
+ "legal-cloths-spend",
+ "legal-otters-punch",
+ "loose-pets-slide",
+ "lucky-days-hug",
+ "many-planes-join",
+ "metal-boxes-laugh",
+ "metal-humans-lose",
+ "modern-taxes-start",
+ "neat-pens-clean",
+ "nice-trams-shake",
+ "old-parks-smell",
+ "open-points-beam",
+ "quiet-hats-sleep",
+ "rotten-melons-sleep",
+ "short-groups-knock",
+ "slick-books-sleep",
+ "slick-onions-wash",
+ "slimy-islands-play",
+ "spicy-teeth-study",
+ "stale-eagles-rush",
+ "tender-dancers-hunt",
+ "tough-lies-grow",
+ "twenty-ducks-relate"
]
}
diff --git a/.changeset/quiet-hats-sleep.md b/.changeset/quiet-hats-sleep.md
new file mode 100644
index 0000000000..5f75f22f6c
--- /dev/null
+++ b/.changeset/quiet-hats-sleep.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder-backend': patch
+---
+
+Fixing OpenAPI definition
diff --git a/.changeset/rare-rice-throw.md b/.changeset/rare-rice-throw.md
new file mode 100644
index 0000000000..6fa0434f67
--- /dev/null
+++ b/.changeset/rare-rice-throw.md
@@ -0,0 +1,5 @@
+---
+'@backstage/core-components': patch
+---
+
+Add `tooltipClasses` prop to `OverflowTooltip` component to allow customisation of the tooltip
diff --git a/.changeset/ready-results-march.md b/.changeset/ready-results-march.md
new file mode 100644
index 0000000000..c28ade8de9
--- /dev/null
+++ b/.changeset/ready-results-march.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog': patch
+---
+
+Fixed `catalogAboutEntityCard` to filter icon links before calling useProps(), preventing side effects from hooks in filtered-out links
diff --git a/.changeset/renovate-7b76d6d.md b/.changeset/renovate-7b76d6d.md
new file mode 100644
index 0000000000..9ccb2dd94b
--- /dev/null
+++ b/.changeset/renovate-7b76d6d.md
@@ -0,0 +1,6 @@
+---
+'@backstage/cli': patch
+'@backstage/plugin-scaffolder-backend': patch
+---
+
+Updated dependency `esbuild` to `^0.27.0`.
diff --git a/.changeset/renovate-959c095.md b/.changeset/renovate-959c095.md
new file mode 100644
index 0000000000..d2370a1c8b
--- /dev/null
+++ b/.changeset/renovate-959c095.md
@@ -0,0 +1,11 @@
+---
+'@backstage/plugin-home-react': patch
+'@backstage/plugin-home': patch
+'@backstage/plugin-scaffolder-react': patch
+'@backstage/plugin-scaffolder': patch
+---
+
+Updated dependency `@rjsf/utils` to `5.24.13`.
+Updated dependency `@rjsf/core` to `5.24.13`.
+Updated dependency `@rjsf/material-ui` to `5.24.13`.
+Updated dependency `@rjsf/validator-ajv8` to `5.24.13`.
diff --git a/.changeset/renovate-97eef4b.md b/.changeset/renovate-97eef4b.md
new file mode 100644
index 0000000000..9b3692bc1c
--- /dev/null
+++ b/.changeset/renovate-97eef4b.md
@@ -0,0 +1,5 @@
+---
+'@backstage/config-loader': patch
+---
+
+Updated dependency `typescript-json-schema` to `^0.67.0`.
diff --git a/.changeset/renovate-a3c2cbd.md b/.changeset/renovate-a3c2cbd.md
new file mode 100644
index 0000000000..2fde85d39e
--- /dev/null
+++ b/.changeset/renovate-a3c2cbd.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder-react': patch
+---
+
+Updated dependency `use-immer` to `^0.11.0`.
diff --git a/.changeset/renovate-ad7bf17.md b/.changeset/renovate-ad7bf17.md
new file mode 100644
index 0000000000..a0f90ff97d
--- /dev/null
+++ b/.changeset/renovate-ad7bf17.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-defaults': patch
+---
+
+Updated dependency `@types/archiver` to `^7.0.0`.
diff --git a/.changeset/renovate-d7ceb06.md b/.changeset/renovate-d7ceb06.md
new file mode 100644
index 0000000000..d22f266301
--- /dev/null
+++ b/.changeset/renovate-d7ceb06.md
@@ -0,0 +1,5 @@
+---
+'@backstage/cli': patch
+---
+
+Updated dependency `@pmmmwh/react-refresh-webpack-plugin` to `^0.6.0`.
diff --git a/.changeset/renovate-ea25c93.md b/.changeset/renovate-ea25c93.md
new file mode 100644
index 0000000000..958e032b64
--- /dev/null
+++ b/.changeset/renovate-ea25c93.md
@@ -0,0 +1,5 @@
+---
+'@backstage/cli': patch
+---
+
+Updated dependency `webpack` to `~5.103.0`.
diff --git a/.changeset/rich-streets-rule.md b/.changeset/rich-streets-rule.md
deleted file mode 100644
index 07f431b6a7..0000000000
--- a/.changeset/rich-streets-rule.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/ui': patch
----
-
-Fix CSS layer ordering in Backstage UI to make sure component styles are loaded after tokens and base declarations.
diff --git a/.changeset/rotten-melons-sleep.md b/.changeset/rotten-melons-sleep.md
new file mode 100644
index 0000000000..c12e0a9669
--- /dev/null
+++ b/.changeset/rotten-melons-sleep.md
@@ -0,0 +1,15 @@
+---
+'@backstage/plugin-catalog-unprocessed-entities-common': patch
+'@backstage/plugin-catalog-unprocessed-entities': patch
+---
+
+Moved types, API and client to the common package, allowing both frontend and
+backend plugins to use the `CatalogUnprocessedEntitiesClient`.
+
+The following types, clients and interfaces have been deprecated and should be
+imported from the `@backstage/plugin-catalog-unprocessed-entities-common` instead:
+`CatalogUnprocessedEntitiesApi`, `CatalogUnprocessedEntitiesApiResponse`, `UnprocessedEntity`,
+`UnprocessedEntityCache`, `UnprocessedEntityError`, `CatalogUnprocessedEntitiesClient`.
+
+All those types, clients and interfaces are re-exported temporarily in the
+`@backstage/plugin-catalog-unprocessed-entities` package until cleaned up.
diff --git a/.changeset/salty-camels-wash.md b/.changeset/salty-camels-wash.md
new file mode 100644
index 0000000000..56600db540
--- /dev/null
+++ b/.changeset/salty-camels-wash.md
@@ -0,0 +1,5 @@
+---
+'@backstage/cli': patch
+---
+
+Fixed internal usage of `yargs`.
diff --git a/.changeset/seven-games-rest.md b/.changeset/seven-games-rest.md
new file mode 100644
index 0000000000..b498c3b138
--- /dev/null
+++ b/.changeset/seven-games-rest.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-techdocs-backend': patch
+---
+
+Corrected `ErrorCallback` type to work with Node 22 types
diff --git a/.changeset/short-groups-knock.md b/.changeset/short-groups-knock.md
new file mode 100644
index 0000000000..0b622a71ed
--- /dev/null
+++ b/.changeset/short-groups-knock.md
@@ -0,0 +1,7 @@
+---
+'@backstage/cli-common': patch
+'@backstage/create-app': patch
+'@backstage/cli': patch
+---
+
+Add proxy configuration from env-vars to create-app tasks
diff --git a/.changeset/short-sides-feel.md b/.changeset/short-sides-feel.md
deleted file mode 100644
index a7e3542ba1..0000000000
--- a/.changeset/short-sides-feel.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/create-app': patch
----
-
-Added the new `@backstage/cli/templates/catalog-provider-module` template to the explicit template configuration for the `next-app` template.
diff --git a/.changeset/silver-garlics-thank.md b/.changeset/silver-garlics-thank.md
deleted file mode 100644
index d95781eb04..0000000000
--- a/.changeset/silver-garlics-thank.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/ui': patch
----
-
-Fix font smoothing as default in Backstage UI.
diff --git a/.changeset/slick-books-sleep.md b/.changeset/slick-books-sleep.md
new file mode 100644
index 0000000000..7aa3be5b03
--- /dev/null
+++ b/.changeset/slick-books-sleep.md
@@ -0,0 +1,5 @@
+---
+'@backstage/theme': patch
+---
+
+Added a `themeName` prop to `UnifiedThemeProvider`, enabling Backstage UI `data-theme-name` CSS attribute to be set based on active theme.
diff --git a/.changeset/slick-onions-wash.md b/.changeset/slick-onions-wash.md
new file mode 100644
index 0000000000..4627d02e81
--- /dev/null
+++ b/.changeset/slick-onions-wash.md
@@ -0,0 +1,5 @@
+---
+'@backstage/core-components': patch
+---
+
+Add i18n support for LogViewer search control
diff --git a/.changeset/slimy-islands-play.md b/.changeset/slimy-islands-play.md
new file mode 100644
index 0000000000..497d86761b
--- /dev/null
+++ b/.changeset/slimy-islands-play.md
@@ -0,0 +1,7 @@
+---
+'@backstage/ui': patch
+---
+
+Added row selection support with visual state styling for hover, selected, and pressed states. Fixed checkbox rendering to only show for multi-select toggle mode.
+
+Affected components: Table, TableHeader, Row, Column
diff --git a/.changeset/slimy-mugs-taste.md b/.changeset/slimy-mugs-taste.md
new file mode 100644
index 0000000000..5773db6853
--- /dev/null
+++ b/.changeset/slimy-mugs-taste.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-test-utils': patch
+---
+
+Fix PostgreSQL 18 `TestDatabases` by pinning the data directory
diff --git a/.changeset/solid-bees-agree.md b/.changeset/solid-bees-agree.md
deleted file mode 100644
index 8feecaf6d4..0000000000
--- a/.changeset/solid-bees-agree.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/eslint-plugin': minor
----
-
-Added `@backstage/no-ui-css-imports-in-non-frontend` rule, which ensures that CSS from `@backstage/ui` is not imported outside of the frontend app.
diff --git a/.changeset/solid-dancers-march.md b/.changeset/solid-dancers-march.md
deleted file mode 100644
index 5633bb3e2e..0000000000
--- a/.changeset/solid-dancers-march.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-user-settings': patch
----
-
-Revert `storageApiRef` implementation
diff --git a/.changeset/sour-bats-press.md b/.changeset/sour-bats-press.md
new file mode 100644
index 0000000000..bfcf3985e6
--- /dev/null
+++ b/.changeset/sour-bats-press.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-auth-node': patch
+---
+
+fix flawed cookie removal logic with chunked tokens
diff --git a/.changeset/spicy-teeth-study.md b/.changeset/spicy-teeth-study.md
new file mode 100644
index 0000000000..03cc8a5440
--- /dev/null
+++ b/.changeset/spicy-teeth-study.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Fixed `useTable` hook to prioritize `providedRowCount` over data length for accurate row count in server-side pagination scenarios.
diff --git a/.changeset/stale-eagles-rush.md b/.changeset/stale-eagles-rush.md
new file mode 100644
index 0000000000..fe640ccd44
--- /dev/null
+++ b/.changeset/stale-eagles-rush.md
@@ -0,0 +1,6 @@
+---
+'@backstage/frontend-plugin-api': patch
+'@backstage/core-plugin-api': patch
+---
+
+Reversed the relationship between the old `@backstage/core-plugin-api` and the new `@backstage/frontend-plugin-api`. Previously, the a lot of API definitions and utilities where defined in the old and re-exported from the old, but this change flips that around so that they now reside in the new package and are re-exported from the old. The external API of both packages remain the same, but this is a step towards being able to add further compatibility with the new frontend system built into the old.
diff --git a/.changeset/stupid-cases-fold.md b/.changeset/stupid-cases-fold.md
new file mode 100644
index 0000000000..5cf0f0f72a
--- /dev/null
+++ b/.changeset/stupid-cases-fold.md
@@ -0,0 +1,5 @@
+---
+'@backstage/cli': patch
+---
+
+Switched ECMAScript version to ES2023.
diff --git a/.changeset/stupid-doodles-love.md b/.changeset/stupid-doodles-love.md
deleted file mode 100644
index 878ad853cd..0000000000
--- a/.changeset/stupid-doodles-love.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@techdocs/cli': patch
----
-
-Fix for missing styles due to move to BUI.
diff --git a/.changeset/tall-ideas-lead.md b/.changeset/tall-ideas-lead.md
new file mode 100644
index 0000000000..60bef903d8
--- /dev/null
+++ b/.changeset/tall-ideas-lead.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder-backend-module-bitbucket-server': patch
+---
+
+Improve error message when provided target branch is missing
diff --git a/.changeset/tame-mirrors-sit.md b/.changeset/tame-mirrors-sit.md
new file mode 100644
index 0000000000..f317185ea2
--- /dev/null
+++ b/.changeset/tame-mirrors-sit.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-dev-utils': patch
+---
+
+Internal update for Node.js v24 support.
diff --git a/.changeset/tender-dancers-hunt.md b/.changeset/tender-dancers-hunt.md
new file mode 100644
index 0000000000..e20ec43ae0
--- /dev/null
+++ b/.changeset/tender-dancers-hunt.md
@@ -0,0 +1,5 @@
+---
+'@backstage/cli': patch
+---
+
+Updated to use new utilities from `@backstage/cli-common`.
diff --git a/.changeset/tender-regions-know.md b/.changeset/tender-regions-know.md
deleted file mode 100644
index 8f59567597..0000000000
--- a/.changeset/tender-regions-know.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-auth': patch
----
-
-Authentication content screen now uses application title.
diff --git a/.changeset/tired-dogs-remain.md b/.changeset/tired-dogs-remain.md
new file mode 100644
index 0000000000..06c193068b
--- /dev/null
+++ b/.changeset/tired-dogs-remain.md
@@ -0,0 +1,12 @@
+---
+'@backstage/plugin-scaffolder-backend-module-rails': patch
+'@backstage/plugin-kubernetes-cluster': patch
+'@techdocs/cli': patch
+'@backstage/cli-common': patch
+'@backstage/create-app': patch
+'@backstage/repo-tools': patch
+'@backstage/codemods': patch
+'@backstage/cli': patch
+---
+
+Bumped dev dependencies `@types/node`
diff --git a/.changeset/tough-lies-grow.md b/.changeset/tough-lies-grow.md
new file mode 100644
index 0000000000..dab952df65
--- /dev/null
+++ b/.changeset/tough-lies-grow.md
@@ -0,0 +1,6 @@
+---
+'@backstage/plugin-techdocs-backend': patch
+'@backstage/plugin-techdocs': patch
+---
+
+The `techdocs` config is now marked as optional.
diff --git a/.changeset/twenty-ducks-relate.md b/.changeset/twenty-ducks-relate.md
new file mode 100644
index 0000000000..359eda66f3
--- /dev/null
+++ b/.changeset/twenty-ducks-relate.md
@@ -0,0 +1,7 @@
+---
+'@backstage/ui': patch
+---
+
+Fixed Table column sorting indicator to show up arrow when no sort is active, correctly indicating that clicking will sort ascending.
+
+Affected components: Column
diff --git a/.changeset/typescript-constructor-refactor.md b/.changeset/typescript-constructor-refactor.md
deleted file mode 100644
index b24cb5dbd0..0000000000
--- a/.changeset/typescript-constructor-refactor.md
+++ /dev/null
@@ -1,63 +0,0 @@
----
-'@backstage/plugin-catalog-backend': patch
-'@backstage/plugin-kubernetes-react': patch
-'@backstage/plugin-notifications-backend': patch
-'@backstage/plugin-notifications-node': patch
-'@backstage/plugin-permission-react': patch
-'@backstage/plugin-scaffolder-backend-module-gcp': patch
-'@backstage/plugin-scaffolder-backend': patch
-'@backstage/plugin-search-backend-module-elasticsearch': patch
-'@backstage/plugin-search-backend-module-pg': patch
-'@backstage/plugin-search-backend': patch
-'@backstage/plugin-search-backend-node': patch
-'@backstage/plugin-search-react': patch
-'@backstage/plugin-signals': patch
-'@backstage/plugin-kubernetes-backend': patch
-'@backstage/plugin-events-backend': patch
-'@backstage/plugin-catalog-backend-module-backstage-openapi': patch
-'@backstage/plugin-events-node': patch
-'@backstage/plugin-user-settings': patch
-'@backstage/plugin-auth-backend': patch
-'@backstage/plugin-auth-backend-module-cloudflare-access-provider': patch
-'@backstage/plugin-catalog-react': patch
-'@backstage/plugin-notifications-backend-module-email': patch
-'@backstage/plugin-events-backend-module-aws-sqs': patch
-'@backstage/plugin-devtools-backend': patch
-'@backstage/plugin-catalog-backend-module-unprocessed': patch
-'@backstage/plugin-catalog-backend-module-azure': patch
-'@backstage/plugin-catalog-backend-module-aws': patch
-'@backstage/plugin-scaffolder': patch
-'@backstage/plugin-techdocs-node': patch
-'@backstage/plugin-catalog-unprocessed-entities': patch
-'@backstage/plugin-catalog-backend-module-msgraph': patch
-'@backstage/plugin-catalog-backend-module-ldap': patch
-'@backstage/plugin-bitbucket-cloud-common': patch
-'@backstage/plugin-auth-node': patch
-'@backstage/backend-app-api': patch
-'@backstage/backend-defaults': patch
-'@backstage/config-loader': patch
-'@backstage/core-app-api': patch
-'@backstage/core-plugin-api': patch
-'@backstage/frontend-app-api': patch
-'@backstage/config': patch
-'@backstage/backend-dynamic-feature-service': patch
-'@backstage/core-components': patch
-'@backstage/cli': patch
-'@backstage/cli-node': patch
-'@backstage/catalog-model': patch
-'@backstage/integration': patch
-'@backstage/frontend-plugin-api': patch
-'@backstage/integration-aws-node': patch
-'@backstage/repo-tools': patch
-'@backstage/backend-test-utils': patch
-'@backstage/plugin-catalog-import': patch
-'@backstage/plugin-events-backend-module-kafka': patch
-'@backstage/plugin-kubernetes-node': patch
-'@backstage/plugin-mcp-actions-backend': patch
-'@backstage/plugin-scaffolder-node-test-utils': patch
-'@backstage/plugin-scaffolder-node': patch
-'@backstage/plugin-scaffolder-react': patch
-'@backstage/plugin-user-settings-backend': patch
----
-
-Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility.
diff --git a/.changeset/warm-moments-repeat.md b/.changeset/warm-moments-repeat.md
deleted file mode 100644
index a72387d720..0000000000
--- a/.changeset/warm-moments-repeat.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/backend-defaults': patch
----
-
-Fix #31348 issue where BitbucketUrlReader ignored provided token and instead always used integration credentials
diff --git a/.changeset/wild-owls-divide.md b/.changeset/wild-owls-divide.md
deleted file mode 100644
index d903b76bd0..0000000000
--- a/.changeset/wild-owls-divide.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/ui': patch
----
-
-Fix default font wight and font family in Backstage UI.
diff --git a/.cursor/rules/tests/backend-test-utils.mdc b/.cursor/rules/tests/backend-test-utils.mdc
new file mode 100644
index 0000000000..857798380d
--- /dev/null
+++ b/.cursor/rules/tests/backend-test-utils.mdc
@@ -0,0 +1,91 @@
+---
+description: Enforce usage of @backstage/backend-test-utils in backend tests
+globs: ['**/*.test.*', '**/*.spec.*']
+alwaysApply: false
+---
+# Backstage Backend Testing Rules
+
+**CRITICAL**: When writing tests for Backstage backend code, you MUST use utilities from `@backstage/backend-test-utils` wherever possible. Do NOT create custom mocks, test databases, caches, or service implementations when equivalent utilities exist in backend-test-utils.
+
+## Required Usage
+
+1. **Service Mocking**: Always use `mockServices` from `@backstage/backend-test-utils` instead of creating custom service mocks. This includes:
+ - `mockServices.auth()` - For authentication service mocking
+ - `mockServices.httpAuth()` - For HTTP authentication service mocking
+ - `mockServices.database()` - For database service mocking
+ - `mockServices.cache()` - For cache service mocking
+ - `mockServices.logger()` - For logger service mocking
+ - `mockServices.permissions()` - For permissions service mocking
+ - `mockServices.scheduler()` - For scheduler service mocking
+ - `mockServices.userInfo()` - For user info service mocking
+ - `mockServices.events()` - For events service mocking
+ - `mockServices.urlReader()` - For URL reader service mocking
+ - `mockServices.rootConfig()` - For root config service mocking
+ - `mockServices.rootLogger()` - For root logger service mocking
+ - `mockServices.rootHealth()` - For root health service mocking
+ - `mockServices.rootLifecycle()` - For root lifecycle service mocking
+ - `mockServices.httpRouter()` - For HTTP router service mocking
+ - `mockServices.lifecycle()` - For lifecycle service mocking
+ - `mockServices.auditor()` - For auditor service mocking
+ - `mockServices.permissionsRegistry()` - For permissions registry service mocking
+
+2. **Credentials**: Always use `mockCredentials` from `@backstage/backend-test-utils` for creating test credentials instead of manually constructing credential objects.
+
+3. **Test Backend**: Always use `startTestBackend` and `TestBackend` from `@backstage/backend-test-utils` when testing backend features, plugins, or modules. This provides a fully configured test backend with all necessary services.
+
+4. **Service Factory Testing**: Use `ServiceFactoryTester` from `@backstage/backend-test-utils` when testing individual service factories in isolation.
+
+5. **Database Testing**: Always use `TestDatabases` from `@backstage/backend-test-utils` for database testing. This supports multiple database engines (PostgreSQL, MySQL, SQLite) and handles ephemeral test database creation and cleanup automatically.
+
+6. **Cache Testing**: Always use `TestCaches` from `@backstage/backend-test-utils` for cache testing. This supports multiple cache backends (Redis, Valkey, Memcache) and handles ephemeral test cache creation and cleanup automatically.
+
+7. **Filesystem Testing**: Use `createMockDirectory` from `@backstage/backend-test-utils` when testing code that interacts with the filesystem. This provides a safe, isolated temporary directory for tests.
+
+8. **MSW Integration**: Use `registerMswTestHooks` from `@backstage/backend-test-utils` when setting up Mock Service Worker for HTTP request mocking in tests.
+
+9. **Error Handler**: Use `mockErrorHandler` from `@backstage/backend-test-utils` when testing Express routers that need error handling middleware.
+
+10. **Alpha Services**: For alpha/experimental services, use the utilities from `@backstage/backend-test-utils/alpha` (e.g., `actionsRegistryServiceMock`, `actionsServiceMock`).
+
+## Import Patterns
+
+Always import from the main package:
+
+```typescript
+import {
+ mockServices,
+ mockCredentials,
+ startTestBackend,
+ TestDatabases,
+ TestCaches,
+ createMockDirectory,
+ registerMswTestHooks,
+ mockErrorHandler,
+ ServiceFactoryTester
+} from '@backstage/backend-test-utils';
+```
+
+For alpha services:
+
+```typescript
+import {
+ actionsRegistryServiceMock,
+ actionsServiceMock
+} from '@backstage/backend-test-utils/alpha';
+```
+
+## When to Use
+
+- **Always** use these utilities when writing backend tests
+- **Never** create custom mocks for services that have equivalents in backend-test-utils
+- **Never** manually set up test databases or caches when TestDatabases/TestCaches are available
+- **Never** create custom test backends when startTestBackend is available
+
+## Benefits
+
+Using backend-test-utils ensures:
+- Consistent test setup across the codebase
+- Proper cleanup and resource management
+- Support for multiple database/cache backends
+- Well-tested and maintained utilities
+- Reduced boilerplate in test code
diff --git a/.cursor/rules/tests/test-utils.mdc b/.cursor/rules/tests/test-utils.mdc
new file mode 100644
index 0000000000..dd66e1f8fa
--- /dev/null
+++ b/.cursor/rules/tests/test-utils.mdc
@@ -0,0 +1,100 @@
+---
+description: Enforce usage of @backstage/test-utils in frontend tests
+globs: ['**/*.test.*', '**/*.spec.*']
+alwaysApply: false
+---
+# Backstage Frontend/Plugin Testing Rules
+
+**CRITICAL**: When writing tests for Backstage frontend code, plugins, or React components, you MUST use utilities from `@backstage/test-utils` wherever possible. Do NOT create custom mocks, test wrappers, or API implementations when equivalent utilities exist in test-utils.
+
+## Required Usage
+
+1. **API Mocking**: Always use `mockApis` from `@backstage/test-utils` instead of creating custom API mocks. This includes:
+ - `mockApis.analytics()` - For AnalyticsApi mocking
+ - `mockApis.config()` - For ConfigApi mocking with optional configuration data
+ - `mockApis.discovery()` - For DiscoveryApi mocking
+ - `mockApis.identity()` - For IdentityApi mocking with user entity ref, token, profile info
+ - `mockApis.permission()` - For PermissionApi mocking with authorization control
+ - `mockApis.storage()` - For StorageApi mocking with in-memory storage
+ - `mockApis.translation()` - For TranslationApi mocking (from alpha)
+ - Each API also provides `.mock()` for jest mocks and `.factory()` for API factories
+
+2. **Test App Wrappers**: Always use `wrapInTestApp` or `renderInTestApp` from `@backstage/test-utils` when testing React components that need Backstage app context (routing, theme, APIs). Use `createTestAppWrapper` for custom wrapper creation.
+
+3. **Test API Provider**: Always use `TestApiProvider` or `TestApiRegistry` from `@backstage/test-utils` when testing components that depend on Backstage APIs. This allows you to provide only the APIs needed for your test.
+
+4. **Async Component Rendering**: Use `renderWithEffects` from `@backstage/test-utils` when testing components that perform asynchronous operations (e.g., useEffect with fetch). This properly handles async rendering with React's act.
+
+5. **Log Collection**: Use `withLogCollector` from `@backstage/test-utils` when you need to capture and assert on console logs, warnings, or errors during tests.
+
+6. **MSW Integration**: Use `registerMswTestHooks` from `@backstage/test-utils` when setting up Mock Service Worker for HTTP request mocking in frontend tests.
+
+7. **Text Content Matcher**: Use `textContentMatcher` from `@backstage/test-utils` as a custom matcher for testing-library queries when matching text content.
+
+8. **Mock Breakpoint**: Use `mockBreakpoint` from `@backstage/test-utils` when testing components that use Material-UI's `useMediaQuery` hook (though this is deprecated in favor of core-components/testUtils).
+
+9. **Individual API Mocks**: Use individual mock APIs when you need more control:
+ - `MockAnalyticsApi` - Analytics API mock
+ - `MockConfigApi` - Config API mock
+ - `MockErrorApi` - Error API mock
+ - `MockFetchApi` - Fetch API mock
+ - `MockPermissionApi` - Permission API mock
+ - `MockStorageApi` - Storage API mock
+ - `MockTranslationApi` - Translation API mock (from alpha)
+
+10. **Alpha APIs**: For alpha/experimental APIs, use the utilities from `@backstage/test-utils/alpha` (e.g., `MockTranslationApi`).
+
+## Import Patterns
+
+Always import from the main package:
+
+```typescript
+import {
+ mockApis,
+ wrapInTestApp,
+ renderInTestApp,
+ createTestAppWrapper,
+ TestApiProvider,
+ TestApiRegistry,
+ renderWithEffects,
+ withLogCollector,
+ registerMswTestHooks,
+ textContentMatcher,
+ mockBreakpoint,
+ MockAnalyticsApi,
+ MockConfigApi,
+ MockErrorApi,
+ MockFetchApi,
+ MockPermissionApi,
+ MockStorageApi
+} from '@backstage/test-utils';
+```
+
+For alpha APIs:
+
+```typescript
+import {
+ MockTranslationApi
+} from '@backstage/test-utils/alpha';
+```
+
+## When to Use
+
+- **Always** use these utilities when writing frontend/plugin tests
+- **Always** use `renderInTestApp` or `wrapInTestApp` for components that need Backstage context
+- **Always** use `TestApiProvider` or `TestApiRegistry` when components depend on Backstage APIs
+- **Always** use `mockApis` instead of creating custom API implementations
+- **Never** manually set up React Router, theme providers, or API contexts when test wrappers are available
+- **Never** create custom API mocks when mockApis provides equivalents
+- **Never** use raw `render` from testing-library when `renderWithEffects` is needed for async components
+
+## Benefits
+
+Using test-utils ensures:
+- Consistent test setup across frontend codebase
+- Proper Backstage app context (routing, theme, APIs)
+- Simplified API mocking with realistic implementations
+- Proper async rendering handling
+- Well-tested and maintained utilities
+- Reduced boilerplate in test code
+- Better integration with Backstage's plugin architecture
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 8491b1a260..40c09a9362 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -11,6 +11,7 @@ yarn.lock @backstage/maintainers @backst
/.changeset/*.md
/.github @backstage/operations-maintainers
/.github/vale @backstage/documentation-maintainers
+/.patches @backstage/operations-maintainers
/beps/0001-notifications-system @backstage/maintainers @backstage/notifications-maintainers
/docs @backstage/maintainers @backstage/documentation-maintainers
/docs/assets/search @backstage/search-maintainers
diff --git a/.github/vale/config/vocabularies/Backstage/accept.txt b/.github/vale/config/vocabularies/Backstage/accept.txt
index 593c7605f5..6697fad858 100644
--- a/.github/vale/config/vocabularies/Backstage/accept.txt
+++ b/.github/vale/config/vocabularies/Backstage/accept.txt
@@ -59,6 +59,7 @@ Chanwit
CI/CD
classname
cli
+CLIs
cloudbuild
Cloudflare
Cloudformation
@@ -110,6 +111,7 @@ dependabot
deps
dequeue
dequeueing
+dereferencing
deserialization
destructured
destructuring
@@ -148,6 +150,7 @@ etag
Expedia
facto
failover
+faq
Fargate
faqs
featureful
@@ -229,6 +232,7 @@ JWTs
Kaewkasi
Kaswell
keepalive
+Keycloak
Keyv
Knex
knip
@@ -323,6 +327,7 @@ onboarded
onboarding
Onboarding
onelogin
+openapi
OpenSearch
OpenShift
openssl
@@ -504,6 +509,7 @@ thumbsup
todo
todos
togglable
+tokenizer
tolerations
Tolerations
toolchain
@@ -569,3 +575,4 @@ Zolotusky
zoomable
zsh
resizable
+enums
diff --git a/.github/workflows/api-breaking-changes-comment.yml b/.github/workflows/api-breaking-changes-comment.yml
index f419a12599..654d73b4d4 100644
--- a/.github/workflows/api-breaking-changes-comment.yml
+++ b/.github/workflows/api-breaking-changes-comment.yml
@@ -23,7 +23,7 @@ jobs:
comment-cache-key: ${{ steps.hash.outputs.COMMENT_FILE_HASH }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
disable-sudo: true
egress-policy: block
@@ -32,7 +32,7 @@ jobs:
- name: 'Download artifacts'
# Fetch output (zip archive) from the workflow run that triggered this workflow.
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
@@ -74,7 +74,7 @@ jobs:
- name: Cache Comment
if: ${{ steps.event.outputs.ACTION != 'closed' }}
- uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
+ uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: comment.md
key: ${{ steps.hash.outputs.COMMENT_FILE_HASH }}
@@ -99,11 +99,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Fetch cached Manifests File
id: cache
- uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
+ uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: comment.md
key: ${{ needs.setup.outputs.comment-cache-key }}
diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml
index f72d9c29bf..934add0716 100644
--- a/.github/workflows/api-breaking-changes.yml
+++ b/.github/workflows/api-breaking-changes.yml
@@ -14,11 +14,11 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# Fetch the commit that's merged into the base rather than the target ref
# This will let us diff only the contents of the PR, without fetching more history
@@ -29,13 +29,13 @@ jobs:
- name: setup-node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
- node-version: 20.x
+ node-version: 22.x
registry-url: https://registry.npmjs.org/
- name: yarn install
uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
with:
- cache-prefix: linux-v20
+ cache-prefix: linux-v22
- name: breaking changes check
run: |
diff --git a/.github/workflows/automate_area-labels.yml b/.github/workflows/automate_area-labels.yml
index f21ef62d8d..e85a9cf11f 100644
--- a/.github/workflows/automate_area-labels.yml
+++ b/.github/workflows/automate_area-labels.yml
@@ -13,11 +13,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
+ - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1
with:
repo-token: '${{ secrets.GITHUB_TOKEN }}'
sync-labels: true
diff --git a/.github/workflows/automate_changeset_feedback.yml b/.github/workflows/automate_changeset_feedback.yml
index c41f4dd8f3..257db3929a 100644
--- a/.github/workflows/automate_changeset_feedback.yml
+++ b/.github/workflows/automate_changeset_feedback.yml
@@ -23,11 +23,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# Fetch the commit that's merged into the base rather than the target ref
# This will let us diff only the contents of the PR, without fetching more history
diff --git a/.github/workflows/automate_merge_message.yml b/.github/workflows/automate_merge_message.yml
index 59da292109..83a1e32fcf 100644
--- a/.github/workflows/automate_merge_message.yml
+++ b/.github/workflows/automate_merge_message.yml
@@ -24,11 +24,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
ref: '${{ github.event.pull_request.merge_commit_sha }}'
@@ -44,7 +44,7 @@ jobs:
node generate.js ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} > message.txt
- name: Post Message
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
ISSUE_NUMBER: ${{ github.event.pull_request.number }}
with:
diff --git a/.github/workflows/automate_stale.yml b/.github/workflows/automate_stale.yml
index 5b92c6211b..a7eef4fae4 100644
--- a/.github/workflows/automate_stale.yml
+++ b/.github/workflows/automate_stale.yml
@@ -15,12 +15,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: Stale check - base
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
+ uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
with:
stale-issue-message: >
This issue has been automatically marked as stale because it has not had
@@ -42,7 +42,7 @@ jobs:
operations-per-run: 100
- name: Stale check - bugs without repro
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
+ uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
with:
stale-issue-message: >
This bug report has been automatically marked as stale because it has not had
diff --git a/.github/workflows/ci-noop.yml b/.github/workflows/ci-noop.yml
index 35901d37b2..12aafe18ee 100644
--- a/.github/workflows/ci-noop.yml
+++ b/.github/workflows/ci-noop.yml
@@ -19,12 +19,12 @@ jobs:
strategy:
matrix:
- node-version: [20.x, 22.x]
+ node-version: [22.x, 24.x]
name: Verify ${{ matrix.node-version }}
steps:
# - name: Harden Runner
- # uses: step-security/harden-runner@8ca2b8b2ece13480cda6dacd3511b49857a23c09 # v2.5.1
+ # uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.5.1
# with:
# egress-policy: audit
@@ -35,12 +35,12 @@ jobs:
strategy:
matrix:
- node-version: [20.x, 22.x]
+ node-version: [22.x, 24.x]
name: Test ${{ matrix.node-version }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 24f08544aa..7552898bd0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -19,7 +19,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [20.x, 22.x]
+ node-version: [22.x, 24.x]
env:
CI: true
@@ -28,11 +28,11 @@ jobs:
name: Install ${{ matrix.node-version }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
@@ -55,7 +55,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [20.x, 22.x]
+ node-version: [22.x, 24.x]
env:
CI: true
@@ -64,11 +64,11 @@ jobs:
name: Verify ${{ matrix.node-version }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
@@ -154,7 +154,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [20.x, 22.x]
+ node-version: [22.x, 24.x]
name: Test ${{ matrix.node-version }}
services:
@@ -210,7 +210,7 @@ jobs:
INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }}
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: fetch master branch
run: git fetch origin master
@@ -236,7 +236,7 @@ jobs:
# Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds
- name: restore backstage-cli cache
- uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
+ uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: .cache/backstage-cli
key: ${{ runner.os }}-v${{ matrix.node-version }}-backstage-cli-${{ github.run_id }}
@@ -260,7 +260,7 @@ jobs:
# Always save success cache even if there were failures, that way it can be used in re-triggered builds
- name: save backstage-cli cache
- uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
+ uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
if: always()
with:
path: .cache/backstage-cli
diff --git a/.github/workflows/cleanup_patch-files.yml b/.github/workflows/cleanup_patch-files.yml
new file mode 100644
index 0000000000..3dc0e27837
--- /dev/null
+++ b/.github/workflows/cleanup_patch-files.yml
@@ -0,0 +1,160 @@
+name: Cleanup Patch Files
+on:
+ push:
+ branches:
+ - 'patch/v*'
+
+concurrency:
+ group: cleanup-patch-files
+ cancel-in-progress: false
+
+jobs:
+ cleanup:
+ name: Cleanup Patch Files
+ runs-on: ubuntu-latest
+ if: github.repository == 'backstage/backstage'
+ permissions:
+ contents: write
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
+ with:
+ egress-policy: audit
+
+ - name: Checkout
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+ with:
+ fetch-depth: 0
+ token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
+
+ - name: Configure Git
+ run: |
+ git config --global user.email noreply@backstage.io
+ git config --global user.name 'Github patch cleanup workflow'
+
+ - name: Extract PR numbers from commit messages
+ id: extract-pr-numbers
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
+ with:
+ github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
+ script: |
+ const { execSync } = require('child_process');
+
+ // Extract PR numbers from commits pushed to the patch branch
+ // Commits have messages in format: "Patch from PR #123"
+ const beforeSha = context.payload.before;
+ const afterSha = context.payload.after;
+
+ let commitRange;
+ if (beforeSha && beforeSha !== '0000000000000000000000000000000000000000') {
+ commitRange = `${beforeSha}..${afterSha}`;
+ } else {
+ // First push to branch, check recent commits
+ commitRange = `-n 20 ${afterSha}`;
+ }
+
+ // Get commit messages from git log
+ let commitMessages;
+ try {
+ commitMessages = execSync(
+ `git log --format=%B ${commitRange}`,
+ { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }
+ );
+ } catch (error) {
+ console.log('No commits found in range');
+ commitMessages = '';
+ }
+
+ // Extract PR numbers from commit messages matching "Patch from PR #123"
+ const prNumbers = new Set();
+ const prPattern = /Patch from PR #(\d+)/g;
+ let match;
+
+ while ((match = prPattern.exec(commitMessages)) !== null) {
+ prNumbers.add(parseInt(match[1], 10));
+ }
+
+ // Convert to sorted array
+ const prNumbersArray = Array.from(prNumbers).sort((a, b) => a - b);
+
+ if (prNumbersArray.length > 0) {
+ console.log(`Found PR numbers: ${prNumbersArray.join(', ')}`);
+ core.setOutput('pr_numbers', JSON.stringify(prNumbersArray));
+ core.setOutput('has_pr_numbers', 'true');
+ } else {
+ console.log('No PR numbers found in commit messages');
+ core.setOutput('pr_numbers', '[]');
+ core.setOutput('has_pr_numbers', 'false');
+ }
+
+ - name: Checkout master
+ if: steps.extract-pr-numbers.outputs.has_pr_numbers == 'true'
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+ with:
+ ref: master
+ token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
+
+ - name: Delete patch files
+ if: steps.extract-pr-numbers.outputs.has_pr_numbers == 'true'
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
+ env:
+ PR_NUMBERS: ${{ steps.extract-pr-numbers.outputs.pr_numbers }}
+ REF_NAME: ${{ github.ref_name }}
+ with:
+ github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
+ script: |
+ const { execSync } = require('child_process');
+ const fs = require('fs');
+ const path = require('path');
+
+ const prNumbers = JSON.parse(process.env.PR_NUMBERS);
+ const patchesDir = path.join(process.cwd(), '.patches');
+
+ if (!fs.existsSync(patchesDir)) {
+ console.log('No .patches directory found, nothing to clean up');
+ return;
+ }
+
+ const deletedFiles = [];
+
+ // Delete patch files for each PR number
+ for (const prNum of prNumbers) {
+ const patchFile = path.join(patchesDir, `pr-${prNum}.txt`);
+ if (fs.existsSync(patchFile)) {
+ fs.unlinkSync(patchFile);
+ deletedFiles.push(patchFile);
+ console.log(`Deleted ${patchFile}`);
+ } else {
+ console.log(`Patch file ${patchFile} not found, skipping`);
+ }
+ }
+
+ if (deletedFiles.length === 0) {
+ console.log('No patch files to delete');
+ return;
+ }
+
+ - name: Commit and push changes
+ if: steps.extract-pr-numbers.outputs.has_pr_numbers == 'true'
+ run: |
+ # Check if there are any changes to commit
+ if git diff --quiet && git diff --cached --quiet; then
+ echo "No changes to commit"
+ exit 0
+ fi
+
+ git add .patches/
+ git commit -m "chore: remove applied patch files after patch release merge
+
+ Removed patch files for PRs included in patch release to ${{ github.ref_name }}"
+
+ git push origin master
+
+ - name: Summary
+ if: steps.extract-pr-numbers.outputs.has_pr_numbers == 'true'
+ run: |
+ echo "## Patch Files Cleanup Summary" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Patch release to ${{ github.ref_name }} has been merged." >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Patch files have been removed from the master branch." >> $GITHUB_STEP_SUMMARY
diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml
index 285c2e6c41..b695c88c90 100644
--- a/.github/workflows/cron.yml
+++ b/.github/workflows/cron.yml
@@ -10,7 +10,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml
index e0f4ec29ed..0cf49b4b8a 100644
--- a/.github/workflows/deploy_docker-image.yml
+++ b/.github/workflows/deploy_docker-image.yml
@@ -16,16 +16,16 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [20.x]
+ node-version: [22.x]
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: checkout
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
path: backstage
ref: ${{ github.event.client_payload.version && env.RELEASE_VERSION || github.ref }}
@@ -52,7 +52,7 @@ jobs:
working-directory: ./example-app
- name: Login to GitHub Container Registry
- uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
+ uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml
index ff0b46f0df..e799bcae00 100644
--- a/.github/workflows/deploy_microsite.yml
+++ b/.github/workflows/deploy_microsite.yml
@@ -23,12 +23,12 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: find latest release
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
id: find-release
with:
script: |
@@ -54,20 +54,20 @@ jobs:
result-encoding: string
- name: checkout latest release
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
ref: refs/tags/${{ steps.find-release.outputs.result }}
- - name: Use Node.js 20.x
+ - name: Use Node.js 22.x
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
- node-version: 20.x
+ node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
with:
- cache-prefix: ${{ runner.os }}-v20.x
+ cache-prefix: ${{ runner.os }}-v22.x
- name: build API reference
run: yarn build:api-docs
@@ -82,7 +82,7 @@ jobs:
# Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds
- name: restore package-docs cache
- uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
+ uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: .cache/package-docs
key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-stable-${{ github.run_id }}
@@ -102,7 +102,7 @@ jobs:
# Always save success cache even if there were failures, that way it can be used in re-triggered builds
- name: save package-docs cache
- uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
+ uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
if: always()
with:
path: .cache/package-docs
@@ -135,23 +135,23 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: checkout master
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- - name: Use Node.js 20.x
+ - name: Use Node.js 22.x
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
- node-version: 20.x
+ node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
with:
- cache-prefix: ${{ runner.os }}-v20.x
+ cache-prefix: ${{ runner.os }}-v22.x
- name: build API reference
run: yarn build:api-docs
@@ -166,7 +166,7 @@ jobs:
# Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds
- name: restore package-docs cache
- uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
+ uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: .cache/package-docs
key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-${{ github.run_id }}
@@ -186,7 +186,7 @@ jobs:
# Always save success cache even if there were failures, that way it can be used in re-triggered builds
- name: save package-docs cache
- uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
+ uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
if: always()
with:
path: .cache/package-docs
@@ -240,19 +240,19 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - name: Use Node.js 20.x
+ - name: Use Node.js 22.x
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
- node-version: 20.x
+ node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
# Stable docs
- name: checkout latest release
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
ref: refs/tags/${{ needs.stable.outputs.release }}
@@ -286,7 +286,7 @@ jobs:
# Next docs
- name: checkout master
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
clean: false
@@ -329,7 +329,7 @@ jobs:
run: ls microsite/build && ls microsite/build/storybook && ls microsite/build/api/stable && ls microsite/build/api/next
- name: Deploy both microsite and storybook to gh-pages
- uses: JamesIves/github-pages-deploy-action@6c2d9db40f9296374acc17b90404b6e8864128c8 # v4.7.3
+ uses: JamesIves/github-pages-deploy-action@4a3abc783e1a24aeb44c16e869ad83caf6b4cc23 # v4.7.4
with:
branch: gh-pages
folder: microsite/build
diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml
index e3e109dfe7..b8bc3fd120 100644
--- a/.github/workflows/deploy_packages.yml
+++ b/.github/workflows/deploy_packages.yml
@@ -13,7 +13,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [20.x, 22.x]
+ node-version: [22.x, 24.x]
services:
postgres18:
@@ -68,7 +68,7 @@ jobs:
INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }}
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
@@ -94,7 +94,7 @@ jobs:
run: yarn backstage-cli config:check --lax
- name: backstage-cli cache
- uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
+ uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: .cache/backstage-cli
key: ${{ runner.os }}-v${{ matrix.node-version }}-backstage-cli-${{ github.run_id }}
@@ -147,11 +147,11 @@ jobs:
strategy:
matrix:
- node-version: [20.x]
+ node-version: [22.x]
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
diff --git a/.github/workflows/issue.yaml b/.github/workflows/issue.yaml
index 17c76c19c2..470ba954c1 100644
--- a/.github/workflows/issue.yaml
+++ b/.github/workflows/issue.yaml
@@ -16,13 +16,13 @@ jobs:
if: github.repository == 'backstage/backstage'
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
# We need to checkout the `.github/ISSUE_TEMPLATE` for the advanced labeler action to be able to read the templates
# While at it we might as well checkout all of `.github` so that the labeling actions don't need to fetch their configs
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
sparse-checkout: .github
@@ -38,7 +38,7 @@ jobs:
# These two steps add labels based on user input in the issue form
- name: Parse issue form
- uses: stefanbuck/github-issue-parser@2ea9b35a8c584529ed00891a8f7e41dc46d0441e # v3
+ uses: stefanbuck/github-issue-parser@25f1485edffc1fee3ea68eb9f59a72e58720ffc4 # v3
id: issue-parser
with:
template-path: .github/ISSUE_TEMPLATE/.common.yaml
diff --git a/.github/workflows/mui-migration-tracker.yml b/.github/workflows/mui-migration-tracker.yml
new file mode 100644
index 0000000000..7ed8013a3b
--- /dev/null
+++ b/.github/workflows/mui-migration-tracker.yml
@@ -0,0 +1,70 @@
+name: MUI to BUI Migration Tracker
+
+on:
+ schedule:
+ # Run daily at midnight UTC
+ - cron: '0 0 * * *'
+ workflow_dispatch:
+ # Allow manual triggering
+
+permissions:
+ issues: write
+ contents: read
+
+jobs:
+ update-migration-progress:
+ runs-on: ubuntu-latest
+ name: Update Migration Progress Issue
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
+ with:
+ egress-policy: audit
+
+ - name: Checkout repository
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
+
+ - name: Setup Node.js
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
+ with:
+ node-version: 22.x
+ registry-url: https://registry.npmjs.org/ # Needed for auth
+
+ - name: yarn install
+ uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
+ with:
+ cache-prefix: ${{ runner.os }}-v22.x
+
+ - name: Run migration analysis
+ id: analysis
+ run: |
+ # Run the migration script and save markdown output
+ yarn mui-to-bui --markdown > migration-report.md
+
+ # Read the report into an environment variable (escape for GitHub Actions)
+ echo "REPORT<> $GITHUB_ENV
+ cat migration-report.md >> $GITHUB_ENV
+ echo "EOF" >> $GITHUB_ENV
+
+ - name: Update GitHub Issue
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const issueNumber = 31467;
+ const reportBody = process.env.REPORT;
+
+ try {
+ await github.rest.issues.update({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: issueNumber,
+ body: reportBody
+ });
+
+ console.log(`✅ Successfully updated issue #${issueNumber}`);
+ } catch (error) {
+ console.error(`❌ Error updating issue: ${error.message}`);
+ throw error;
+ }
diff --git a/.github/workflows/pr-review-comment-trigger.yaml b/.github/workflows/pr-review-comment-trigger.yaml
index d5970deae8..95c9c921f5 100644
--- a/.github/workflows/pr-review-comment-trigger.yaml
+++ b/.github/workflows/pr-review-comment-trigger.yaml
@@ -20,7 +20,7 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
diff --git a/.github/workflows/pr-review-comment.yaml b/.github/workflows/pr-review-comment.yaml
index 9db0792eaa..bb7bd26c53 100644
--- a/.github/workflows/pr-review-comment.yaml
+++ b/.github/workflows/pr-review-comment.yaml
@@ -17,13 +17,13 @@ jobs:
steps:
# Inspired by https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: Read PR Number
id: pr-number
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml
index 33dd3d00ce..36988f7d07 100644
--- a/.github/workflows/pr.yaml
+++ b/.github/workflows/pr.yaml
@@ -18,7 +18,7 @@ jobs:
if: github.repository == 'backstage/backstage' && ( github.event.pull_request || github.event.issue.pull_request )
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 232d055072..96cb3368be 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -29,17 +29,17 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: 'Checkout code'
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- name: 'Run analysis'
- uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
+ uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
@@ -67,6 +67,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
- uses: github/codeql-action/upload-sarif@a4e1a019f5e24960714ff6296aee04b736cbc3cf # v3.29.6
+ uses: github/codeql-action/upload-sarif@497990dfed22177a82ba1bbab381bc8f6d27058f # v3.31.6
with:
sarif_file: results.sarif
diff --git a/.github/workflows/sync_canon.yml b/.github/workflows/sync_canon.yml
index 3b85c1c49d..9da6c80b98 100644
--- a/.github/workflows/sync_canon.yml
+++ b/.github/workflows/sync_canon.yml
@@ -8,26 +8,26 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: Checkout
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- - name: Use Node.js 20.x
+ - name: Use Node.js 22.x
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
- node-version: 20.x
+ node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
with:
- cache-prefix: ${{ runner.os }}-v20.x
+ cache-prefix: ${{ runner.os }}-v22.x
- name: Checkout backstage/docs-ui
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
repository: backstage/docs-ui
path: bui-external-docs
diff --git a/.github/workflows/sync_code-formatting.yml b/.github/workflows/sync_code-formatting.yml
index 33cab94280..9719a34886 100644
--- a/.github/workflows/sync_code-formatting.yml
+++ b/.github/workflows/sync_code-formatting.yml
@@ -10,11 +10,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# Fetch changes to previous commit - required for 'only_changed' in Prettier action
fetch-depth: 0
diff --git a/.github/workflows/sync_dependabot-changesets.yml b/.github/workflows/sync_dependabot-changesets.yml
index 181d48eb38..9e894631cc 100644
--- a/.github/workflows/sync_dependabot-changesets.yml
+++ b/.github/workflows/sync_dependabot-changesets.yml
@@ -11,12 +11,12 @@ jobs:
if: github.actor == 'dependabot[bot]' && github.repository == 'backstage/backstage'
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: Checkout
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 2
ref: ${{ github.head_ref }}
@@ -26,7 +26,7 @@ jobs:
git config --global user.email noreply@backstage.io
git config --global user.name 'Github changeset workflow'
- name: Generate changeset
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const { promises: fs } = require('fs');
diff --git a/.github/workflows/sync_patch-release.yml b/.github/workflows/sync_patch-release.yml
new file mode 100644
index 0000000000..e17a229256
--- /dev/null
+++ b/.github/workflows/sync_patch-release.yml
@@ -0,0 +1,252 @@
+name: Sync Patch Release
+on:
+ push:
+ branches:
+ - master
+ paths:
+ - '.patches/**'
+ pull_request:
+ branches:
+ - master
+ paths:
+ - '.patches/**'
+
+concurrency:
+ group: sync-patch-release
+ cancel-in-progress: true
+
+jobs:
+ sync-patch-release:
+ name: Sync Patch Release
+ runs-on: ubuntu-latest
+ if: github.repository == 'backstage/backstage'
+ permissions:
+ contents: write
+ pull-requests: write
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
+ with:
+ egress-policy: audit
+
+ - name: Checkout
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+ with:
+ fetch-depth: 20000
+ fetch-tags: true
+ token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
+
+ - name: Configure Git
+ run: |
+ git config --global user.email noreply@backstage.io
+ git config --global user.name 'Github patch release workflow'
+
+ - name: Check for patch files
+ id: check-patches
+ run: |
+ if [ -d ".patches" ] && [ "$(ls -A .patches/pr-*.txt 2>/dev/null)" ]; then
+ echo "has_patches=true" >> $GITHUB_OUTPUT
+ echo "Found patch files"
+ else
+ echo "has_patches=false" >> $GITHUB_OUTPUT
+ echo "No patch files found"
+ fi
+
+ - name: Find existing PR
+ id: find-pr
+ if: steps.check-patches.outputs.has_patches == 'true'
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
+ with:
+ github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
+ script: |
+ const branchName = 'patch-release';
+ const owner = context.repo.owner;
+ const repo = context.repo.repo;
+
+ // Find PR with the branch as head
+ const { data: pulls } = await github.rest.pulls.list({
+ owner,
+ repo,
+ head: `${owner}:${branchName}`,
+ state: 'open',
+ });
+
+ if (pulls.length > 0) {
+ console.log(`Found existing PR: #${pulls[0].number}`);
+ core.setOutput('pr_number', pulls[0].number.toString());
+ core.setOutput('pr_exists', 'true');
+ } else {
+ console.log('No existing PR found');
+ core.setOutput('pr_exists', 'false');
+ }
+
+ - name: Close PR and delete branch if no patches
+ if: steps.check-patches.outputs.has_patches == 'false'
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
+ with:
+ github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
+ script: |
+ const branchName = 'patch-release';
+ const owner = context.repo.owner;
+ const repo = context.repo.repo;
+
+ // Find PR with the branch as head
+ const { data: pulls } = await github.rest.pulls.list({
+ owner,
+ repo,
+ head: `${owner}:${branchName}`,
+ state: 'open',
+ });
+
+ if (pulls.length > 0) {
+ console.log(`Closing PR #${pulls[0].number}`);
+ await github.rest.pulls.update({
+ owner,
+ repo,
+ pull_number: pulls[0].number,
+ state: 'closed',
+ });
+ }
+
+ // Delete the branch
+ try {
+ await github.rest.git.deleteRef({
+ owner,
+ repo,
+ ref: `heads/${branchName}`,
+ });
+ console.log(`Deleted branch ${branchName}`);
+ } catch (error) {
+ if (error.status === 422) {
+ console.log(`Branch ${branchName} does not exist, skipping deletion`);
+ } else {
+ throw error;
+ }
+ }
+
+ - name: Install Dependencies
+ if: steps.check-patches.outputs.has_patches == 'true'
+ run: yarn --immutable
+
+ - name: Read patch files for PR metadata
+ if: steps.check-patches.outputs.has_patches == 'true'
+ id: read-patches
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
+ with:
+ github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
+ script: |
+ // Read patch files from master branch to generate PR body
+ const fs = require('fs').promises;
+ const path = require('path');
+
+ const patchesDir = path.join(process.cwd(), '.patches');
+ let files = [];
+ try {
+ files = await fs.readdir(patchesDir);
+ } catch (error) {
+ console.log(`Error reading patches directory: ${error.message}`);
+ core.setOutput('pr_body', 'This patch release fixes issues.');
+ return;
+ }
+
+ const patchFiles = files.filter(f => /^pr-\d+\.txt$/.test(f));
+
+ const patches = [];
+ for (const file of patchFiles) {
+ const match = file.match(/^pr-(\d+)\.txt$/);
+ if (match) {
+ const prNumber = parseInt(match[1], 10);
+ const filePath = path.join(patchesDir, file);
+ let content = '';
+ try {
+ content = await fs.readFile(filePath, 'utf8');
+ } catch (error) {
+ console.log(`Error reading ${file}: ${error.message}`);
+ }
+ const description = content.trim() || '';
+ patches.push({ prNumber, description });
+ }
+ }
+
+ if (patches.length === 0) {
+ console.log('No patches found, using default values');
+ core.setOutput('pr_body', 'This patch release fixes issues.');
+ core.setOutput('pr_numbers', '[]');
+ return;
+ }
+
+ patches.sort((a, b) => a.prNumber - b.prNumber);
+ const prNumbers = patches.map(p => p.prNumber);
+ const descriptions = patches.map(p => p.description);
+
+ const descriptionList = descriptions.map(desc => `- ${desc}`).join('\n');
+ const body = `This patch release fixes the following issues:\n\n${descriptionList}`;
+
+ core.setOutput('pr_body', body);
+ core.setOutput('pr_numbers', JSON.stringify(prNumbers));
+
+ - name: Run patch release script
+ if: steps.check-patches.outputs.has_patches == 'true'
+ id: run-script
+ env:
+ GITHUB_TOKEN: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
+ HUSKY: '0'
+ PATCH_RELEASE_BRANCH: patch-release
+ run: |
+ # The script will handle finding the release version, creating the patch branch,
+ # and deleting patch files. Run the patch script with --from-dir flag to read from .patches/ directory
+ # The script will write the patch branch to GITHUB_OUTPUT
+ node scripts/patch-release-for-pr.js --from-dir
+
+ # Ensure the branch is pushed (script pushes it, but force push in case of conflicts)
+ git push origin patch-release --force
+
+ - name: Create or update PR
+ if: steps.check-patches.outputs.has_patches == 'true'
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
+ env:
+ PR_EXISTS: ${{ steps.find-pr.outputs.pr_exists }}
+ PR_NUMBER: ${{ steps.find-pr.outputs.pr_number }}
+ PR_BODY: ${{ steps.read-patches.outputs.pr_body }}
+ PATCH_BRANCH: ${{ steps.run-script.outputs.patch_branch }}
+ with:
+ github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
+ script: |
+ const branchName = 'patch-release';
+ const owner = context.repo.owner;
+ const repo = context.repo.repo;
+
+ const title = 'Patch Release';
+ const body = process.env.PR_BODY;
+ const prExists = process.env.PR_EXISTS === 'true';
+ const prNumber = process.env.PR_NUMBER;
+ const patchBranch = process.env.PATCH_BRANCH;
+
+ if (prExists && prNumber) {
+ console.log(`Updating PR #${prNumber}`);
+ await github.rest.pulls.update({
+ owner,
+ repo,
+ pull_number: parseInt(prNumber, 10),
+ title,
+ body,
+ });
+ } else {
+ // Use the patch branch from the script
+ if (!patchBranch) {
+ throw new Error('Patch branch not provided from script. Make sure the script outputs the patch branch name.');
+ }
+
+ const baseBranch = patchBranch;
+ console.log(`Creating new PR with base branch: ${baseBranch}`);
+
+ const { data: pr } = await github.rest.pulls.create({
+ owner,
+ repo,
+ title,
+ body,
+ head: branchName,
+ base: baseBranch,
+ });
+ console.log(`Created PR #${pr.number}`);
+ }
diff --git a/.github/workflows/sync_release-manifest.yml b/.github/workflows/sync_release-manifest.yml
index a9c4b3722b..7452505a22 100644
--- a/.github/workflows/sync_release-manifest.yml
+++ b/.github/workflows/sync_release-manifest.yml
@@ -8,26 +8,26 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: Checkout
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# 'v' prefix is added here for the tag, we keep it out of the manifest logic
ref: v${{ github.event.client_payload.version }}
- - name: Use Node.js 20.x
+ - name: Use Node.js 22.x
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
- node-version: 20.x
+ node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
with:
- cache-prefix: ${{ runner.os }}-v20.x
+ cache-prefix: ${{ runner.os }}-v22.x
- name: Build yarn plugin
working-directory: packages/yarn-plugin
@@ -35,7 +35,7 @@ jobs:
# Checkout backstage/versions into /backstage/versions, which is where store the output
- name: Checkout versions
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
repository: backstage/versions
path: versions
@@ -71,7 +71,7 @@ jobs:
git push
- name: Dispatch update-helper update
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
# TODO(Rugvip): Remove the create-app dispatch once we've been on the release version for a while
diff --git a/.github/workflows/sync_renovate-changesets.yml b/.github/workflows/sync_renovate-changesets.yml
index 2331e2c30f..07f2e24697 100644
--- a/.github/workflows/sync_renovate-changesets.yml
+++ b/.github/workflows/sync_renovate-changesets.yml
@@ -11,12 +11,12 @@ jobs:
if: github.actor == 'renovate[bot]' && github.repository == 'backstage/backstage'
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: Checkout
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 2
ref: ${{ github.head_ref }}
@@ -26,7 +26,7 @@ jobs:
git config --global user.email noreply@backstage.io
git config --global user.name 'Github changeset workflow'
- name: Generate changeset
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const { promises: fs } = require("fs");
diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml
index 3716c1f48b..97f7e8ed14 100644
--- a/.github/workflows/sync_snyk-github-issues.yml
+++ b/.github/workflows/sync_snyk-github-issues.yml
@@ -12,24 +12,24 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- - name: Use Node.js 20.x
+ - name: Use Node.js 22.x
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
- node-version: 20.x
+ node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
with:
- cache-prefix: ${{ runner.os }}-v20.x
+ cache-prefix: ${{ runner.os }}-v22.x
- name: Create Snyk report
- uses: snyk/actions/node@77490d94e966421e076e95ad8fa87aa55e5ca409 # master
+ uses: snyk/actions/node@9adf32b1121593767fc3c057af55b55db032dc04 # master
continue-on-error: true # Snyk CLI exits with error when vulnerabilities are found
with:
args: >
diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml
index 205d146bb8..8b76338645 100644
--- a/.github/workflows/sync_snyk-monitor.yml
+++ b/.github/workflows/sync_snyk-monitor.yml
@@ -25,13 +25,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Monitor and Synchronize Snyk Policies
- uses: snyk/actions/node@77490d94e966421e076e95ad8fa87aa55e5ca409 # master
+ uses: snyk/actions/node@9adf32b1121593767fc3c057af55b55db032dc04 # master
with:
command: monitor
args: >
@@ -46,7 +46,7 @@ jobs:
# Above we run the `monitor` command, this runs the `test` command which is
# the one that generates the SARIF report that we can upload to GitHub.
- name: Create Snyk report
- uses: snyk/actions/node@77490d94e966421e076e95ad8fa87aa55e5ca409 # master
+ uses: snyk/actions/node@9adf32b1121593767fc3c057af55b55db032dc04 # master
continue-on-error: true # To make sure that SARIF upload gets called
with:
args: >
@@ -58,6 +58,6 @@ jobs:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
NODE_OPTIONS: --max-old-space-size=7168
- name: Upload Snyk report
- uses: github/codeql-action/upload-sarif@a4e1a019f5e24960714ff6296aee04b736cbc3cf # v3.29.6
+ uses: github/codeql-action/upload-sarif@497990dfed22177a82ba1bbab381bc8f6d27058f # v3.31.6
with:
sarif_file: snyk.sarif
diff --git a/.github/workflows/sync_version-packages.yml b/.github/workflows/sync_version-packages.yml
index 66a798bd58..683d527e45 100644
--- a/.github/workflows/sync_version-packages.yml
+++ b/.github/workflows/sync_version-packages.yml
@@ -12,20 +12,28 @@ jobs:
create-release-pr:
name: Create Changeset PR
runs-on: ubuntu-latest
+
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 20000
fetch-tags: true
token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
+ - name: Use Node.js 22.x
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
+ with:
+ node-version: 22.x
+ registry-url: https://registry.npmjs.org/ # Needed for auth
+
- name: Install Dependencies
run: yarn --immutable
+
- name: Create Release Pull Request
uses: backstage/changesets-action@a39baf18913e669734ffb00c2fd9900472cfa240 # v2.3.2
with:
diff --git a/.github/workflows/verify_accessibility-noop.yml b/.github/workflows/verify_accessibility-noop.yml
index df6f004171..ee696ebc7f 100644
--- a/.github/workflows/verify_accessibility-noop.yml
+++ b/.github/workflows/verify_accessibility-noop.yml
@@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
diff --git a/.github/workflows/verify_accessibility.yml b/.github/workflows/verify_accessibility.yml
index 48e243b002..82d26ca510 100644
--- a/.github/workflows/verify_accessibility.yml
+++ b/.github/workflows/verify_accessibility.yml
@@ -20,19 +20,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- - name: Use Node.js 20.x
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+ - name: Use Node.js 22.x
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
- node-version: 20.x
+ node-version: 22.x
- name: yarn install
uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
with:
- cache-prefix: ${{ runner.os }}-v20.x
+ cache-prefix: ${{ runner.os }}-v22.x
- name: run Lighthouse CI
run: |
yarn dlx @lhci/cli@0.11.x autorun
diff --git a/.github/workflows/verify_chromatic-noop.yml b/.github/workflows/verify_chromatic-noop.yml
index 4de52a3258..35015def52 100644
--- a/.github/workflows/verify_chromatic-noop.yml
+++ b/.github/workflows/verify_chromatic-noop.yml
@@ -20,7 +20,7 @@ jobs:
name: Chromatic
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
diff --git a/.github/workflows/verify_chromatic.yml b/.github/workflows/verify_chromatic.yml
index 0c9f110b4c..a9d83b01e3 100644
--- a/.github/workflows/verify_chromatic.yml
+++ b/.github/workflows/verify_chromatic.yml
@@ -19,17 +19,17 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
- node-version: [20.x]
+ node-version: [22.x]
name: Chromatic
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: Checkout code
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 10000 # Required to retrieve git history
@@ -64,21 +64,29 @@ jobs:
- name: Prepare Chromatic Message
if: github.event_name == 'pull_request' && steps.chromatic.outputs.url
- id: prepare-message
+ id: prepareMessage
run: |
if [ "${{ steps.chromatic.outputs.changeCount }}" = "0" ] || [ -z "${{ steps.chromatic.outputs.changeCount }}" ]; then
- echo "changes-text=No visual changes detected - [**View build in Chromatic**](${{ steps.chromatic.outputs.buildUrl }})" >> $GITHUB_OUTPUT
+ echo "changesText=No visual changes detected - [**View build in Chromatic**](${{ steps.chromatic.outputs.buildUrl }})" >> $GITHUB_OUTPUT
else
- echo "changes-text=**${{ steps.chromatic.outputs.changeCount }}** visual changes are waiting for review - [**Review changes in Chromatic**](${{ steps.chromatic.outputs.buildUrl }})" >> $GITHUB_OUTPUT
+ echo "changesText=**${{ steps.chromatic.outputs.changeCount }}** visual changes are waiting for review - [**Review changes in Chromatic**](${{ steps.chromatic.outputs.buildUrl }})" >> $GITHUB_OUTPUT
fi
- - name: Post Chromatic Link in PR Comment
+ - name: Post Chromatic Results to Job Summary
if: github.event_name == 'pull_request' && steps.chromatic.outputs.url
+ run: |
+ echo "## 🎨 Visual Testing with Chromatic" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "- ${{ steps.prepareMessage.outputs.changesText }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **${{ steps.chromatic.outputs.specCount}}** stories for **${{ steps.chromatic.outputs.componentCount}}** Components - [**Preview changes in Storybook**](${{ steps.chromatic.outputs.storybookUrl }})" >> $GITHUB_STEP_SUMMARY
+
+ - name: Post Chromatic Link in PR Comment
+ if: github.event_name == 'pull_request' && steps.chromatic.outputs.url && github.event.pull_request.head.repo.full_name == github.repository
uses: mshick/add-pr-comment@v2
with:
message: |
## 🎨 Visual Testing with Chromatic
- - ${{ steps.prepare-message.outputs.changes-text }}
+ - ${{ steps.prepareMessage.outputs.changesText }}
- **${{ steps.chromatic.outputs.specCount}}** stories for **${{ steps.chromatic.outputs.componentCount}}** Components - [**Preview changes in Storybook**](${{ steps.chromatic.outputs.storybookUrl }})
repo-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml
index 254938ecc8..d06c8ea270 100644
--- a/.github/workflows/verify_codeql.yml
+++ b/.github/workflows/verify_codeql.yml
@@ -42,12 +42,12 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: Checkout repository
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
@@ -55,7 +55,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@a4e1a019f5e24960714ff6296aee04b736cbc3cf # v3.29.6
+ uses: github/codeql-action/init@497990dfed22177a82ba1bbab381bc8f6d27058f # v3.31.6
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -66,7 +66,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@a4e1a019f5e24960714ff6296aee04b736cbc3cf # v3.29.6
+ uses: github/codeql-action/autobuild@497990dfed22177a82ba1bbab381bc8f6d27058f # v3.31.6
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -80,4 +80,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@a4e1a019f5e24960714ff6296aee04b736cbc3cf # v3.29.6
+ uses: github/codeql-action/analyze@497990dfed22177a82ba1bbab381bc8f6d27058f # v3.31.6
diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml
index 6c855eeea4..8810f05a16 100644
--- a/.github/workflows/verify_docs-quality.yml
+++ b/.github/workflows/verify_docs-quality.yml
@@ -12,11 +12,11 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
# Vale does not support file excludes, so we use the script to generate a list of files instead
# The action also does not allow args or a local config file to be passed in, so the files array
diff --git a/.github/workflows/verify_e2e-linux-noop.yml b/.github/workflows/verify_e2e-linux-noop.yml
index aa3358f2f7..152f4cc2c0 100644
--- a/.github/workflows/verify_e2e-linux-noop.yml
+++ b/.github/workflows/verify_e2e-linux-noop.yml
@@ -24,12 +24,12 @@ jobs:
strategy:
matrix:
- node-version: [20.x, 22.x]
+ node-version: [22.x, 24.x]
name: E2E Linux ${{ matrix.node-version }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml
index dc81d42e8a..436995ddc2 100644
--- a/.github/workflows/verify_e2e-linux.yml
+++ b/.github/workflows/verify_e2e-linux.yml
@@ -34,7 +34,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [20.x, 22.x]
+ node-version: [22.x, 24.x]
env:
CI: true
@@ -43,11 +43,11 @@ jobs:
name: E2E Linux ${{ matrix.node-version }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Configure Git
run: |
diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml
index e933efe3f8..07c751e098 100644
--- a/.github/workflows/verify_e2e-techdocs.yml
+++ b/.github/workflows/verify_e2e-techdocs.yml
@@ -23,7 +23,7 @@ jobs:
strategy:
matrix:
- node-version: [20.x, 22.x]
+ node-version: [22.x, 24.x]
env:
CI: true
@@ -32,11 +32,11 @@ jobs:
name: Techdocs
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: '3.9'
diff --git a/.github/workflows/verify_e2e-windows-noop.yml b/.github/workflows/verify_e2e-windows-noop.yml
index b1a4344bfc..f04748a950 100644
--- a/.github/workflows/verify_e2e-windows-noop.yml
+++ b/.github/workflows/verify_e2e-windows-noop.yml
@@ -20,12 +20,12 @@ jobs:
strategy:
matrix:
- node-version: [20.x, 22.x]
+ node-version: [22.x, 24.x]
name: E2E Windows ${{ matrix.node-version }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml
index eefcf22b35..933bbb0840 100644
--- a/.github/workflows/verify_e2e-windows.yml
+++ b/.github/workflows/verify_e2e-windows.yml
@@ -24,7 +24,7 @@ jobs:
strategy:
matrix:
- node-version: [20.x, 22.x]
+ node-version: [22.x, 24.x]
env:
CI: true
@@ -33,7 +33,7 @@ jobs:
name: E2E Windows ${{ matrix.node-version }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
@@ -44,7 +44,7 @@ jobs:
git config --global core.autocrlf false
git config --global core.eol lf
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Configure Git
run: |
diff --git a/.github/workflows/verify_fossa.yml b/.github/workflows/verify_fossa.yml
index 9c811fbe9f..681ac2c07a 100644
--- a/.github/workflows/verify_fossa.yml
+++ b/.github/workflows/verify_fossa.yml
@@ -14,12 +14,12 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: Checkout
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Install Fossa
run: "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | bash"
diff --git a/.github/workflows/verify_microsite-noop.yml b/.github/workflows/verify_microsite-noop.yml
index 3466826cae..70686e3bd9 100644
--- a/.github/workflows/verify_microsite-noop.yml
+++ b/.github/workflows/verify_microsite-noop.yml
@@ -21,7 +21,7 @@ jobs:
name: Microsite
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml
index f3d48ef480..20a6e705cd 100644
--- a/.github/workflows/verify_microsite.yml
+++ b/.github/workflows/verify_microsite.yml
@@ -28,12 +28,12 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: find latest release
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
id: find-release
with:
script: |
@@ -59,20 +59,20 @@ jobs:
result-encoding: string
- name: checkout latest release
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
ref: refs/tags/${{ steps.find-release.outputs.result }}
- - name: Use Node.js 20.x
+ - name: Use Node.js 22.x
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
- node-version: 20.x
+ node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
with:
- cache-prefix: ${{ runner.os }}-v20.x
+ cache-prefix: ${{ runner.os }}-v22.x
- name: build API reference
run: yarn build:api-docs
@@ -87,7 +87,7 @@ jobs:
# Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds
- name: restore package-docs cache
- uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
+ uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: .cache/package-docs
key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-stable-${{ github.run_id }}
@@ -105,7 +105,7 @@ jobs:
# Always save success cache even if there were failures, that way it can be used in re-triggered builds
- name: save package-docs cache
- uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
+ uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
if: always()
with:
path: .cache/package-docs
@@ -137,23 +137,23 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- name: checkout master
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- - name: Use Node.js 20.x
+ - name: Use Node.js 22.x
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
- node-version: 20.x
+ node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
with:
- cache-prefix: ${{ runner.os }}-v20.x
+ cache-prefix: ${{ runner.os }}-v22.x
- name: build API reference
run: yarn build:api-docs
@@ -168,7 +168,7 @@ jobs:
# Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds
- name: restore package-docs cache
- uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
+ uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: .cache/package-docs
key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-next-${{ github.run_id }}
@@ -186,7 +186,7 @@ jobs:
# Always save success cache even if there were failures, that way it can be used in re-triggered builds
- name: save package-docs cache
- uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4
+ uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
if: always()
with:
path: .cache/package-docs
@@ -234,16 +234,16 @@ jobs:
name: Microsite
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- - name: Use Node.js 20.x
+ - name: Use Node.js 22.x
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
- node-version: 20.x
+ node-version: 22.x
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.9'
@@ -295,7 +295,7 @@ jobs:
run: yarn build:api-docs
- name: checkout master
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
clean: false
diff --git a/.github/workflows/verify_microsite_accessibility-noop.yml b/.github/workflows/verify_microsite_accessibility-noop.yml
index 5e0bc35cc0..bb13a85dfa 100644
--- a/.github/workflows/verify_microsite_accessibility-noop.yml
+++ b/.github/workflows/verify_microsite_accessibility-noop.yml
@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
diff --git a/.github/workflows/verify_microsite_accessibility.yml b/.github/workflows/verify_microsite_accessibility.yml
index 40e1866b5f..0c428ae359 100644
--- a/.github/workflows/verify_microsite_accessibility.yml
+++ b/.github/workflows/verify_microsite_accessibility.yml
@@ -15,16 +15,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- - name: Use Node.js 20.x
+ - name: Use Node.js 22.x
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
- node-version: 20.x
+ node-version: 22.x
- name: top-level install
run: yarn install --immutable
diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml
index 363bc075b8..ae2ec477d3 100644
--- a/.github/workflows/verify_windows.yml
+++ b/.github/workflows/verify_windows.yml
@@ -17,7 +17,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- node-version: [20.x, 22.x]
+ node-version: [22.x, 24.x]
env:
CI: true
@@ -29,11 +29,11 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
with:
egress-policy: audit
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
diff --git a/.github/workflows/welcome.yml b/.github/workflows/welcome.yml
new file mode 100644
index 0000000000..9061b70d79
--- /dev/null
+++ b/.github/workflows/welcome.yml
@@ -0,0 +1,42 @@
+name: Add a welcome comment
+
+on:
+ pull_request_target:
+ types: [opened]
+
+permissions:
+ issues: write
+ pull-requests: write
+ contents: read
+
+jobs:
+ welcome:
+ runs-on: ubuntu-latest
+ if: github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
+ steps:
+ - name: Add a welcome comment
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const pr = context.payload.pull_request
+ const login = pr.user.login
+
+ const message = `
+ Hi @${login}, thanks for opening your first pull request in Backstage! 👋
+
+ A couple of useful links to help you get started:
+
+ * [Contributing Guide](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md)
+ * [DCO Sign-off Instructions](https://github.com/backstage/backstage/blob/master/DCO)
+ * [Style Guide](https://github.com/backstage/backstage/blob/master/STYLE.md)
+
+ We really appreciate your contribution and look forward to reviewing your work. Welcome aboard!
+ `
+
+ await github.rest.issues.createComment({
+ issue_number: pr.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: message
+ })
diff --git a/.patches/README.md b/.patches/README.md
new file mode 100644
index 0000000000..19b599d6bb
--- /dev/null
+++ b/.patches/README.md
@@ -0,0 +1,16 @@
+# Patch Release Process
+
+This directory tracks patches to be applied to the next patch release. It contains a list of patch files of the format `pr-.txt` that define which pull requests should be included in the next patch release, where the content of the file is the description of the fix.
+
+The [sync_patch-release.yml](/.github/workflows/sync_patch-release.yml) workflow will automatically create a "Patch Release" PR with the patches in this directory.
+
+## Usage
+
+To add a PR to the set of patches, run `yarn patch-pr ` in the root of the repository.
+
+## GitHub Workflow
+
+A GitHub workflow automatically keeps a "Patch Release" PR in sync with the patches listed in this directory. When you add, modify, or remove patch files, the workflow will:
+
+- Update the existing PR if patch files exist
+- Close and delete the PR branch if no patch files exist
diff --git a/.patches/create-pr-patch.js b/.patches/create-pr-patch.js
new file mode 100755
index 0000000000..528d4fc404
--- /dev/null
+++ b/.patches/create-pr-patch.js
@@ -0,0 +1,15 @@
+#!/usr/bin/env node
+
+const fs = require('fs');
+const path = require('path');
+
+const prNumber = process.argv[2];
+const description = process.argv.slice(3).join(' ');
+
+if (!prNumber || !description) {
+ console.error('Usage: yarn patch-pr ');
+ process.exit(1);
+}
+
+const patchFilePath = path.join(__dirname, `pr-${prNumber}.txt`);
+fs.writeFileSync(patchFilePath, description);
diff --git a/ADOPTERS.md b/ADOPTERS.md
index 208cd370ba..2b3cad0b25 100644
--- a/ADOPTERS.md
+++ b/ADOPTERS.md
@@ -94,7 +94,7 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/
| [IKEA IT AB](https://www.ingka.com) | [@bjornramberg](https://github.com/bjornramberg), [@supriyachitale](https://github.com/supriyachitale) | Supporting engineers at scale with self serve access and connecting the dots of our engineering platform and services, enabling product teams to move faster and go further, and unleashing innovation, reuse and co-creation across the organisation. |
| [Invitae](https://www.invitae.com/en) | [@ryan-hanchett](https://github.com/ryan-hanchett), [@gmandler42](https://github.com/gmandler42) | Centralized Developer Experience portal, putting all of our tooling behind a single pane of glass and creating a living service catalog. |
| [Fortum](https://www.fortum.com/) | [@brunoamaroalmeida](https://github.com/brunoamaroalmeida), [@dhaval-vithalani](https://github.com/dhaval-vithalani), [@sunilkumarmohanty](https://github.com/sunilkumarmohanty) | A central portal containing information about our applications, services, processes and other software assets to be used by Fortum software engineering community. |
-| [Procore](https://www.procore.com/jobs/engineering) | [@shayon](https://github.com/Shayon), [@jamesdabbs-procore](https://github.com/jamesdabbs-procore) | Developer portal - centralized software catalog and templates to enable self serve and reduce cognitive load. |
+| [Procore](https://www.procore.com/jobs/engineering) | [@shayon](https://github.com/Shayon), [@kurtaking](https://github.com/kurtaking), [@pc-bob](https://github.com/pc-bob) | Developer portal - centralized software catalog and templates to enable self serve and reduce cognitive load. |
| [Bradesco](https://banco.bradesco/html/classic/sobre/index.shtm) | [@danilosoarescardoso](https://github.com/danilosoarescardoso) | Centralized developer portal with software catalog, software templates, techdocs and some plugins from community and by our own. |
| [SeatGeek](https://seatgeek.com) | [@zhammer](https://github.com/zhammer) | Software catalog and documentation platform |
| [Grupo Boticário](https://www.grupoboticario.com.br/) | [@chicoribas](https://github.com/chicoribas), [@fndiaz](https://github.com/fndiaz), [@digogid](https://github.com/digogid), [@manumbs](https://github.com/manumbs), [@haooliveira84](https://github.com/haooliveira84), [@Rhullyam](https://github.com/Rhullyam) | Centralized developer portal with catalog, documentation, and software templates to build a ready to go application, including pipelines (CI/CD) and cloud services provisioning |
@@ -287,3 +287,4 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/
| [Syngenta Digital](https://www.syngentadigital.com) | [Bitan Mallick](https://www.linkedin.com/in/bitanmallick) | Internal developer portal, designed to empower developers and streamline workflows. We use software catalog, tech-radar, software templates, tech-docs and various custom plugins to ensure an efficient and collaborative development experience. |
| [Sophotech](https://sopho.tech) | [@archy-rock3t-cloud](https://github.com/archy-rock3t-cloud), [Artem Muterko](mailto:artem@sopho.tech) | Custom Developer Platform based on Backstage, providing a service catalog, infrastructure templates, and integrated tooling to give developers a self-service experience. |
| [Swiss Mobiliar Insurance Company Ltd.](https://www.mobiliar.ch/) | [Patrick Wyler](mailto:patrick.wyler@mobiliar.ch) and [Beat Winistörfer](mailto:beat.winistoerfer@mobiliar.ch) | The portal provides a unified interface for accessing all relevant DevOps information previously scattered across various locations, enhancing accessibility and clarity for all IT employees. It relies on an internal graph database that enhances the Backstage software catalog with many additional elements. Significant effort has been invested in the visual representation of information through graphs and diagrams, facilitating analysis and improving the understanding of dependencies. |
+| [DB Systel GmbH](https://www.dbsystel.de/dbsystel-en/) | [DB Systel GmbH](https://github.com/dbsystel) | Deutsche Bahn's Internal Developer Portal leverages Backstage and Crossplane.io to deliver a fully GitOps-driven onboarding experience for engineering teams across the company. Our Scaffolder ecosystem accelerates platform setup (Artifactory, GitLab, OpenShift), service bootstrapping, automated testing, AI-ready backends, and modern frontend development. We also make heavy use of custom catalog modules and frontend plugins that support developers in checking their provisioned resources or security and compliance of their code. |
diff --git a/OWNERS.md b/OWNERS.md
index d4ee3c6400..44b4ebca15 100644
--- a/OWNERS.md
+++ b/OWNERS.md
@@ -45,6 +45,20 @@ Scope: The catalog plugin and catalog model
| Patrik Oldsberg | Spotify | Cubic Belugas | [Rugvip](https://github.com/Rugvip) | `Rugvip#0019` |
| Vincenzo Scamporlino | Spotify | Cubic Belugas | [vinzscam](http://github.com/vinzscam) | `vinzscam#6944` |
+### Community Plugins
+
+Team: @backstage/community-plugins-maintainers
+
+Scope: Tooling and Community Repo Maintainers for the Backstage [Community Plugins repository](https://github.com/backstage/community-plugins)
+
+| Name | Organization | GitHub | Discord |
+| -------------------- | ------------- | ------------------------------------------- | ------------ |
+| André Wanlin | Spotify | [awanlin](https://github.com/awanlin) | `ahhhndre` |
+| Bethany Griggs | Red Hat | [BethGriggs](https://github.com/BethGriggs) | `bethgriggs` |
+| Kashish Mittal | Red Hat | [04kash](https://github.com/04kash) | `kashh._.` |
+| Vincenzo Scamporlino | Spotify | [vinzscam](https://github.com/vinzscam) | `vinzscam` |
+| Peter Macdonald | VodafoneZiggo | [Parsifal-M](https://github.com/Parsifal-M) | `parsifal` |
+
### Design System
Team: @backstage/design-system-maintainers
@@ -206,20 +220,6 @@ Scope: The auditor core service, along with auditor usage in the main repository
| ------------ | ------------ | ----------------------------------------------- | -------------- |
| Paul Schultz | Red Hat | [schultzp2020](https://github.com/schultzp2020) | `schultzp2020` |
-### Community Plugins
-
-Team: @backstage/community-plugins-maintainers
-
-Scope: Tooling and Community Repo Maintainers for the Backstage [Community Plugins repository](https://github.com/backstage/community-plugins)
-
-| Name | Organization | GitHub | Discord |
-| -------------------- | ------------- | ------------------------------------------- | ------------ |
-| André Wanlin | Spotify | [awanlin](https://github.com/awanlin) | `ahhhndre` |
-| Bethany Griggs | Red Hat | [BethGriggs](https://github.com/BethGriggs) | `bethgriggs` |
-| Kashish Mittal | Red Hat | [04kash](https://github.com/04kash) | `kashh._.` |
-| Vincenzo Scamporlino | Spotify | [vinzscam](https://github.com/vinzscam) | `vinzscam` |
-| Peter Macdonald | VodafoneZiggo | [Parsifal-M](https://github.com/Parsifal-M) | `parsifal` |
-
### Events
Team: @backstage/events-maintainers
diff --git a/README-fr_FR.md b/README-fr_FR.md
index a54e9c8c49..27a7cd1153 100644
--- a/README-fr_FR.md
+++ b/README-fr_FR.md
@@ -41,7 +41,7 @@ Pour commencer à utiliser Backstage, consultez [Getting Started documentation](
## Documentation
-La documentation de Backstage inclus:
+La documentation de Backstage inclut:
- [Main documentation](https://backstage.io/docs)
- [Software Catalog](https://backstage.io/docs/features/software-catalog/)
diff --git a/README.md b/README.md
index ba44ca951d..e3e9bd77dd 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,3 @@
-> [!NOTE]
-> 🏖 From Monday October 27th through November 3rd, maintainers and Spotify employees will be on vacation due to Wellness Week. Expect the project to move a little slower than normal, and support to be limited. Normal service will resume after that! 🏝
-
[](https://backstage.io/)
# [Backstage](https://backstage.io)
diff --git a/app-config.yaml b/app-config.yaml
index b45255d26c..c1a894dcc7 100644
--- a/app-config.yaml
+++ b/app-config.yaml
@@ -110,7 +110,7 @@ integrations:
### Example for how to add a bitbucket cloud integration
# bitbucketCloud:
# - username: ${BITBUCKET_USERNAME}
- # appPassword: ${BITBUCKET_APP_PASSWORD}
+ # token: ${BITBUCKET_API_TOKEN}
### Example for how to add your bitbucket server instance using the API:
# - host: server.bitbucket.com
# apiBaseUrl: server.bitbucket.com
@@ -209,6 +209,11 @@ scaffolder:
email: scaffolder@backstage.io
# Use to customize the default commit message when new components are created
defaultCommitMessage: 'Initial commit'
+ defaultEnvironment:
+ parameters:
+ region: eu-west-1
+ secrets:
+ environment: ${NODE_ENV}
auth:
experimentalDynamicClientRegistration:
diff --git a/contrib/catalog/ImmediateEntityProvider.ts b/contrib/catalog/ImmediateEntityProvider.ts
index b5580b775a..9f9f986567 100644
--- a/contrib/catalog/ImmediateEntityProvider.ts
+++ b/contrib/catalog/ImmediateEntityProvider.ts
@@ -1,3 +1,8 @@
+import {
+ coreServices,
+ createBackendModule,
+ LoggerService,
+} from '@backstage/backend-plugin-api';
import {
ANNOTATION_LOCATION,
ANNOTATION_ORIGIN_LOCATION,
@@ -10,50 +15,18 @@ import {
EntityProvider,
EntityProviderConnection,
} from '@backstage/plugin-catalog-node';
-import { parseEntityYaml } from '@backstage/plugin-catalog-backend';
+import { parseEntityYaml } from '@backstage/plugin-catalog-node';
+import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';
import bodyParser from 'body-parser';
import express from 'express';
import Router from 'express-promise-router';
import lodash from 'lodash';
-import { Logger } from 'winston';
/**
* An entity provider attached to a router, that lets users perform direct
* manipulation of a set of entities using REST requests.
- *
- * @remarks
- *
- * Installation:
- *
- * Add it to the catalog builder in your
- * `packages/backend/src/plugins/catalog.ts`. Note that it BOTH adds a provider
- * and amends the catalog router:
- *
- * ```
- * const immediate = new ImmediateEntityProvider({
- * logger: env.logger,
- * handleEntity: (deferred) => {
- * // Optionally modify the incoming entity
- * },
- * });
- * builder.addEntityProvider(immediate);
- *
- * // ...
- *
- * return router.use('/immediate', immediate.getRouter());
- * ```
- *
- * API (assume a catalog prefix, e.g. `/api/catalog`):
- *
- * - `POST /immediate/entities`: Accepts a YAML document of entities, and
- * inserts or updates the entities that match that document. Returns 201 OK on
- * success.
- *
- * - `PUT /immediate/entities`: Accepts a YAML document of entities, and
- * replaces the entire set of entities managed by the provider with those
- * entities. Returns 201 OK on success.
*/
-export class ImmediateEntityProvider implements EntityProvider {
+class ImmediateEntityProvider implements EntityProvider {
private connection?: EntityProviderConnection;
private readonly entityValidator: (data: unknown) => Entity;
@@ -76,7 +49,7 @@ export class ImmediateEntityProvider implements EntityProvider {
router.use(bodyParser.raw({ type: '*/*' }));
- router.post('/entities', async (req, res) => {
+ router.post('/immediate/entities', async (req, res) => {
if (!this.connection) {
throw new Error(`Service is not yet initialized`);
}
@@ -89,7 +62,7 @@ export class ImmediateEntityProvider implements EntityProvider {
res.status(201).end();
});
- router.put('/entities', async (req, res) => {
+ router.put('/immediate/entities', async (req, res) => {
if (!this.connection) {
throw new Error(`Service is not yet initialized`);
}
@@ -151,19 +124,59 @@ export class ImmediateEntityProvider implements EntityProvider {
/**
* Options for {@link ImmediateEntityProvider}.
*/
-export interface ImmediateEntityProviderOptions {
+interface ImmediateEntityProviderOptions {
/**
- * The logger to use.
+ * The logger.
*/
- logger: Logger;
+ logger: LoggerService;
/**
- * An optional function to perform adjustments to, or validate, an incoming
- * entity before being stored. It is permitted to modify the deferred entity,
- * but the request is static and has had its body consumed.
+ * An optional callback function to perform adjustments to, or validate, an
+ * incoming entity before being stored. It is permitted to modify the deferred
+ * entity, but the request is static and has had its body consumed.
*/
handleEntity?: (
request: express.Request,
deferred: DeferredEntity,
) => void | Promise;
}
+
+/**
+ * Backend module that installs an immediate entity provider.
+ *
+ * @remarks
+ *
+ * Install it by doing `backend.add(immediateEntityProviderModule)` in your `packages/backend/src/index.ts` file.
+ *
+ * API:
+ *
+ * - `POST /api/catalog/immediate/entities`: Accepts a YAML document of entities, and
+ * inserts or updates the entities that match that document. Returns 201 OK on
+ * success.
+ *
+ * - `PUT /api/catalog/immediate/entities`: Accepts a YAML document of entities, and
+ * replaces the entire set of entities managed by the provider with those
+ * entities. Returns 201 OK on success.
+ */
+export const immediateEntityProviderModule = createBackendModule({
+ pluginId: 'catalog',
+ moduleId: 'immediate-entity-provider',
+ register(env) {
+ env.registerInit({
+ deps: {
+ logger: coreServices.logger,
+ router: coreServices.httpRouter,
+ catalogProcessing: catalogProcessingExtensionPoint,
+ },
+ async init({ logger, router, catalogProcessing }) {
+ const provider = new ImmediateEntityProvider({
+ logger,
+ // add handleEntity here if you need to modify incoming entities before they are stored
+ });
+
+ catalogProcessing.addEntityProvider(provider);
+ router.use(provider.getRouter());
+ },
+ });
+ },
+});
diff --git a/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild b/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild
index 47fd506a34..6c87681391 100644
--- a/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild
+++ b/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild
@@ -6,7 +6,7 @@
# This dockerfile also performs the build first inside docker. This may come
# with a build time impact, but is sometimes desirable. If you want to run the
-# build on the host instead, use the file simply named Dockerfile in this folder
+# build on the host instead, use the file simply named Dockerfile.hostbuild in this folder
# instead.
# USAGE:
diff --git a/contrib/docs/tutorials/heroku-deployment.md b/contrib/docs/tutorials/heroku-deployment.md
index 2dea1f021c..8d0b8740b7 100644
--- a/contrib/docs/tutorials/heroku-deployment.md
+++ b/contrib/docs/tutorials/heroku-deployment.md
@@ -49,7 +49,7 @@ Add a build script in `package.json` to compile frontend during deployment:
## Start Command
-Create a [Procfile](https://devcenter.heroku.com/articles/procfile) in the app's root:
+Create a [`Procfile`](https://devcenter.heroku.com/articles/procfile) in the app's root:
```shell
echo "web: yarn workspace backend start --config ../../app-config.yaml --config ../../app-config.production.yaml" > Procfile
diff --git a/docker-compose.deps.yml b/docker-compose.deps.yml
index c7515eca04..fa7937fdf1 100644
--- a/docker-compose.deps.yml
+++ b/docker-compose.deps.yml
@@ -2,7 +2,7 @@
name: backstage
services:
psql:
- image: postgres:17.6
+ image: postgres:17.7
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
@@ -24,7 +24,7 @@ services:
timeout: 3s
retries: 5
opensearch:
- image: opensearchproject/opensearch:2.19.3
+ image: opensearchproject/opensearch:2.19.4
environment:
plugins.security.disabled: true
discovery.type: single-node
diff --git a/docs-ui/README.md b/docs-ui/README.md
index 41b9921413..b943f1ce7c 100644
--- a/docs-ui/README.md
+++ b/docs-ui/README.md
@@ -13,3 +13,28 @@ yarn start
## Deployment
Deployments are done automatically when a PR is merged into the `master` branch. We host the website using Github pages.
+
+## Maintaining Component Changelogs
+
+After a `@backstage/ui` release, sync the component changelogs to keep documentation up-to-date:
+
+```bash
+yarn sync:changelog
+```
+
+This script:
+
+- Parses `packages/ui/CHANGELOG.md` for new versions
+- Extracts entries tagged with "Affected components: ..."
+- Updates `src/utils/changelog.ts` with new entries
+- Handles both component-specific and general package changes
+
+After running, review the changes in `src/utils/changelog.ts` and commit them.
+
+**Preview changes before writing:**
+
+```bash
+yarn sync:changelog:dry-run
+```
+
+Running this gives you a summary of what would be written, without actually adding or changing any files.
diff --git a/docs-ui/next.config.mjs b/docs-ui/next.config.mjs
index 8e6ed8fdc0..2ff341b609 100644
--- a/docs-ui/next.config.mjs
+++ b/docs-ui/next.config.mjs
@@ -1,4 +1,6 @@
import createMDX from '@next/mdx';
+import path from 'path';
+import { fileURLToPath } from 'url';
const nextConfig = {
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
@@ -13,6 +15,10 @@ const nextConfig = {
// These are type-level conflicts that don't affect runtime behavior
ignoreBuildErrors: true,
},
+ outputFileTracingRoot: path.join(
+ path.dirname(fileURLToPath(import.meta.url)),
+ '..',
+ ),
};
const withMDX = createMDX({});
diff --git a/docs-ui/package.json b/docs-ui/package.json
index 17c13f64f3..ff5d59e7f0 100644
--- a/docs-ui/package.json
+++ b/docs-ui/package.json
@@ -7,7 +7,9 @@
"build": "next build",
"lint": "next lint",
"prestart": "yarn sync:css",
- "start": "concurrently \"yarn sync:css:watch\" \"next dev\"",
+ "start": "next dev",
+ "sync:changelog": "node scripts/sync-changelog.mjs",
+ "sync:changelog:dry-run": "node scripts/sync-changelog.mjs --dry-run",
"sync:css": "node scripts/sync-css.js",
"sync:css:watch": "node scripts/sync-css.js --watch"
},
@@ -16,19 +18,21 @@
"@types/react-dom": "19.1.7"
},
"dependencies": {
+ "@base-ui-components/react": "^1.0.0-beta.4",
"@codemirror/lang-sass": "^6.0.2",
"@codemirror/view": "^6.34.4",
"@lezer/highlight": "^1.2.1",
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
- "@next/mdx": "15.3.4",
+ "@next/mdx": "15.5.7",
"@remixicon/react": "^4.6.0",
"@storybook/react": "^8.6.12",
"@uiw/codemirror-themes": "^4.23.7",
"@uiw/react-codemirror": "^4.23.7",
+ "clsx": "^2.1.1",
"html-react-parser": "^5.2.5",
"motion": "^12.4.1",
- "next": "15.4.7",
+ "next": "15.5.7",
"next-mdx-remote-client": "^2.1.2",
"prop-types": "^15.8.1",
"react": "19.1.1",
@@ -37,16 +41,17 @@
"storybook": "^8.6.12"
},
"devDependencies": {
+ "@octokit/rest": "^22.0.1",
"@shikijs/transformers": "^3.13.0",
"@types/mdx": "^2.0.13",
- "@types/node": "^20",
+ "@types/node": "^22.13.14",
"@types/react": "19.1.9",
"@types/react-dom": "19.1.7",
"chokidar": "^3.6.0",
- "concurrently": "^8.2.2",
"eslint": "^8",
- "eslint-config-next": "15.3.4",
+ "eslint-config-next": "15.5.7",
"lightningcss": "^1.28.2",
- "typescript": "^5"
+ "typescript": "^5",
+ "unified": "^11.0.4"
}
}
diff --git a/docs-ui/scripts/sync-changelog.mjs b/docs-ui/scripts/sync-changelog.mjs
new file mode 100644
index 0000000000..5eab3616dd
--- /dev/null
+++ b/docs-ui/scripts/sync-changelog.mjs
@@ -0,0 +1,991 @@
+#!/usr/bin/env node
+/* eslint-disable no-restricted-syntax */
+
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+import { unified } from 'unified';
+import remarkParse from 'remark-parse';
+import { Octokit } from '@octokit/rest';
+import { execSync } from 'child_process';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+/**
+ * Compare two semantic versions.
+ * Returns: 1 if versionA > versionB, -1 if versionA < versionB, 0 if equal
+ * Handles pre-release versions (e.g., "0.9.0-next.2")
+ */
+function compareVersions(versionA, versionB) {
+ // Strip 'v' prefix if present
+ versionA = versionA.replace(/^v/, '');
+ versionB = versionB.replace(/^v/, '');
+
+ // Split into [major.minor.patch, prerelease]
+ const [versionABase, versionAPre] = versionA.split('-');
+ const [versionBBase, versionBPre] = versionB.split('-');
+
+ // Compare base versions
+ const versionAParts = versionABase.split('.').map(Number);
+ const versionBParts = versionBBase.split('.').map(Number);
+
+ for (
+ let i = 0;
+ i < Math.max(versionAParts.length, versionBParts.length);
+ i++
+ ) {
+ const versionAPart = versionAParts[i] || 0;
+ const versionBPart = versionBParts[i] || 0;
+
+ if (versionAPart > versionBPart) return 1;
+ if (versionAPart < versionBPart) return -1;
+ }
+
+ // If base versions equal, compare pre-release
+ if (!versionAPre && !versionBPre) return 0;
+ if (!versionAPre) return 1; // versionA is release, versionB is pre-release
+ if (!versionBPre) return -1; // versionA is pre-release, versionB is release
+
+ // Both have pre-release, compare them
+ return versionAPre.localeCompare(versionBPre);
+}
+
+/**
+ * Check if versionA is newer than versionB
+ */
+function isNewerVersion(versionA, versionB) {
+ return compareVersions(versionA, versionB) > 0;
+}
+
+/**
+ * Normalize version to base version (remove pre-release suffix)
+ * "0.9.0-next.3" → "0.9.0"
+ * "0.9.0" → "0.9.0"
+ */
+function normalizeVersion(version) {
+ return version.split('-')[0];
+}
+
+/**
+ * Read the existing changelog.ts and find the highest version number
+ * by scanning the changelogs/ directory for version files
+ */
+function getLastSyncedVersion(changelogPath) {
+ try {
+ // Scan changelogs/ directory for latest version file
+ const changelogsDir = path.join(path.dirname(changelogPath), 'changelogs');
+
+ if (!fs.existsSync(changelogsDir)) {
+ return null;
+ }
+
+ // Read all version files (v0.1.0.ts, v0.2.0.ts, etc.)
+ const files = fs
+ .readdirSync(changelogsDir)
+ .filter(f => f.match(/^v\d+\.\d+\.\d+\.ts$/))
+ .map(f => f.replace(/^v/, '').replace(/\.ts$/, ''));
+
+ if (files.length === 0) {
+ return null;
+ }
+
+ // Find highest version
+ let highest = files[0];
+ for (const version of files) {
+ if (compareVersions(version, highest) > 0) {
+ highest = version;
+ }
+ }
+
+ return highest;
+ } catch (error) {
+ console.error(`Error reading changelog file: ${error.message}`);
+ return null;
+ }
+}
+
+/**
+ * Convert PascalCase component name to kebab-case slug
+ * Avatar -> avatar
+ * ButtonIcon -> button-icon
+ * SearchField -> searchfield (special case per existing data)
+ */
+function pascalToKebab(str) {
+ // Special cases observed in changelog.ts
+ const specialCases = {
+ SearchField: 'searchfield',
+ TextField: 'textfield',
+ DataTable: 'datatable',
+ ScrollArea: 'scrollarea',
+ };
+
+ if (specialCases[str]) {
+ return specialCases[str];
+ }
+
+ // General conversion: insert hyphen before uppercase letters
+ return str
+ .replace(/([A-Z])/g, '-$1')
+ .toLowerCase()
+ .replace(/^-/, ''); // Remove leading hyphen
+}
+
+/**
+ * Map component name to valid Component type slug
+ * Returns null if component not recognized
+ */
+function mapComponentName(name, validComponents) {
+ const kebab = pascalToKebab(name.trim());
+ const kebabLower = kebab.toLowerCase();
+
+ // Check if it's a valid component
+ const valid = validComponents.find(c => c.toLowerCase() === kebabLower);
+
+ return valid || null;
+}
+
+/**
+ * Get valid component names from types.ts Component type
+ */
+function getValidComponents(changelogPath) {
+ // Read from types.ts instead of changelog.ts
+ const typesPath = changelogPath.replace('changelog.ts', 'types.ts');
+ const content = fs.readFileSync(typesPath, 'utf-8');
+
+ // Extract Component type union
+ const typeMatch = content.match(/export type Component =([^;]+);/s);
+ if (!typeMatch) {
+ throw new Error('Could not find Component type definition');
+ }
+
+ // Extract string literals from the union
+ const components = typeMatch[1]
+ .match(/['"]([^'"]+)['"]/g)
+ .map(s => s.replace(/['"]/g, ''));
+
+ return components;
+}
+
+/**
+ * Parse CHANGELOG.md and extract entries newer than sinceVersion
+ */
+async function parseChangelogMd(changelogPath, sinceVersion, validComponents) {
+ const content = fs.readFileSync(changelogPath, 'utf-8');
+ const tree = unified().use(remarkParse).parse(content);
+
+ const entries = [];
+ let currentVersion = null;
+ let currentSection = null; // 'Minor Changes', 'Patch Changes', 'Major Changes'
+ let processedCount = 0;
+
+ // Walk through the markdown AST
+ async function walk(node, depth = 0) {
+ // Version headers (## 0.9.0)
+ if (node.type === 'heading' && node.depth === 2) {
+ const versionText = extractText(node).trim();
+ // Validate that this is actually a version number (X.Y.Z or X.Y.Z-pre.N)
+ // Skip headings that are just markdown content within changelog entries
+ if (/^\d+\.\d+\.\d+/.test(versionText)) {
+ currentVersion = versionText;
+ currentSection = null;
+ }
+ return;
+ }
+
+ // Section headers (### Minor Changes)
+ if (node.type === 'heading' && node.depth === 3) {
+ const sectionText = extractText(node);
+ currentSection = sectionText.trim();
+ return;
+ }
+
+ // List items (- 539cf26: description)
+ if (node.type === 'listItem' && currentVersion && currentSection) {
+ // Only process if version is newer than sinceVersion
+ if (!sinceVersion || isNewerVersion(currentVersion, sinceVersion)) {
+ const entry = await parseListItem(
+ node,
+ currentVersion,
+ currentSection,
+ validComponents,
+ content,
+ );
+ if (entry) {
+ entries.push(entry);
+ processedCount++;
+ // Progress logging every 50 entries
+ if (processedCount % 50 === 0) {
+ process.stdout.write(`\r Processed ${processedCount} entries...`);
+ }
+ }
+ }
+ }
+
+ // Recurse into children
+ if (node.children) {
+ for (const child of node.children) {
+ await walk(child, depth + 1);
+ }
+ }
+ }
+
+ await walk(tree);
+
+ // Clear progress line
+ if (processedCount > 0) {
+ process.stdout.write(`\r Processed ${processedCount} entries - done!\n`);
+ }
+
+ return entries;
+}
+
+/**
+ * Extract plain text from a markdown node
+ */
+function extractText(node) {
+ if (node.type === 'text') {
+ return node.value;
+ }
+ if (node.children) {
+ return node.children.map(extractText).join('');
+ }
+ return '';
+}
+
+/**
+ * Extract raw markdown content from a node using position offsets
+ * This preserves all formatting: code blocks, lists, bold/italic, etc.
+ */
+function extractMarkdown(node, sourceText) {
+ if (!node.position) {
+ return extractText(node); // Fallback to text extraction
+ }
+
+ const start = node.position.start.offset;
+ const end = node.position.end.offset;
+
+ if (start === undefined || end === undefined) {
+ return extractText(node);
+ }
+
+ return sourceText.slice(start, end).trim();
+}
+
+/**
+ * Parse a list item to extract changelog entry
+ * NOTE: PR numbers are NOT fetched here - they're fetched later only for new entries
+ */
+async function parseListItem(
+ node,
+ version,
+ section,
+ validComponents,
+ sourceText,
+) {
+ // Extract the full text content for SHA parsing
+ const fullText = extractText(node);
+
+ // Parse commit SHA and description
+ // Format: "- 539cf26: description..."
+ const match = fullText.match(/^-?\s*([a-f0-9]+):\s*(.+)/s);
+ if (!match) {
+ return null;
+ }
+
+ const commitSha = match[1];
+
+ // Extract full markdown content from the list item
+ let fullMarkdown = extractMarkdown(node, sourceText);
+
+ // Remove the commit SHA prefix from markdown
+ let description = fullMarkdown.replace(/^-?\s*[a-f0-9]+:\s*/, '').trim();
+
+ // Auto-detect components from "Affected components:" line
+ let components = [];
+ const componentMatch = description.match(/Affected components?:\s*([^\n]+)/i);
+ if (componentMatch) {
+ const componentNames = componentMatch[1]
+ .split(',')
+ .map(name => name.trim())
+ .filter(Boolean);
+
+ components = componentNames
+ .map(name => mapComponentName(name, validComponents))
+ .filter(Boolean);
+
+ // Optionally strip "Affected components:" line from description
+ description = description
+ .replace(/\n*Affected components?:[ \t]*[^\n]+/i, '')
+ .trim();
+ }
+
+ const prs = []; // Will be populated later by fetchPRNumbers()
+
+ // Infer type from section and description
+ const type = inferChangeType(section, description, version);
+
+ return {
+ version: normalizeVersion(version),
+ section,
+ commitSha,
+ description,
+ components,
+ prs,
+ type,
+ };
+}
+
+/**
+ * Infer change type from section and description
+ */
+function inferChangeType(section, description, version) {
+ const isPre1 = version.startsWith('0.');
+
+ // Check description keywords first
+ if (description.match(/^(New|Add(ed)?)\s/i)) {
+ return 'new';
+ }
+ if (description.includes('BREAKING')) {
+ return 'breaking';
+ }
+
+ // Infer from section
+ if (section === 'Minor Changes') {
+ return isPre1 ? 'breaking' : undefined;
+ } else if (section === 'Major Changes') {
+ return 'breaking';
+ } else if (section === 'Patch Changes') {
+ return 'fix';
+ }
+
+ return undefined;
+}
+
+/**
+ * Generate new changelog entries and update changelog.ts
+ */
+/**
+ * Fetch PR numbers for entries that need them
+ * Only fetches for entries with empty prs array (new entries)
+ */
+async function fetchPRNumbers(entries, dryRun = false) {
+ const entriesNeedingPRs = entries.filter(
+ e => e.prs.length === 0 && e.commitSha,
+ );
+
+ if (entriesNeedingPRs.length === 0) {
+ return;
+ }
+
+ console.log(
+ `\n🔍 Fetching PR numbers for ${entriesNeedingPRs.length} new entries...`,
+ );
+
+ let fetchedCount = 0;
+ const startTime = Date.now();
+
+ for (const entry of entriesNeedingPRs) {
+ const prNumber = await findPRNumber(entry.commitSha);
+ if (prNumber) {
+ entry.prs.push(prNumber);
+ }
+
+ fetchedCount++;
+ // Progress every 10 lookups
+ if (fetchedCount % 10 === 0 || fetchedCount === entriesNeedingPRs.length) {
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
+ const rate = ((fetchedCount / (Date.now() - startTime)) * 1000).toFixed(
+ 1,
+ );
+ process.stdout.write(
+ `\r Fetched ${fetchedCount}/${entriesNeedingPRs.length} PR numbers (${elapsed}s, ~${rate} req/s)`,
+ );
+ }
+ }
+
+ const totalTime = ((Date.now() - startTime) / 1000).toFixed(1);
+ const foundCount = entriesNeedingPRs.filter(e => e.prs.length > 0).length;
+ console.log(
+ `\n ✓ Found ${foundCount}/${entriesNeedingPRs.length} PR numbers in ${totalTime}s`,
+ );
+}
+
+/**
+ * Generate per-version changelog files in changelogs/ directory
+ */
+async function generateVersionFiles(entries, changelogsDir, dryRun = false) {
+ // Group entries by normalized version
+ const byVersion = {};
+ entries.forEach(entry => {
+ const version = entry.version;
+ if (!byVersion[version]) {
+ byVersion[version] = [];
+ }
+ byVersion[version].push(entry);
+ });
+
+ const versionFiles = [];
+ const skippedVersions = [];
+ const generatedVersions = [];
+
+ for (const [version, versionEntries] of Object.entries(byVersion)) {
+ const fileName = `v${version}.ts`;
+ const filePath = path.join(changelogsDir, fileName);
+ const varName = `changelog_${version.replace(/\./g, '_')}`;
+
+ // Check if file already exists - skip if it does
+ if (fs.existsSync(filePath)) {
+ skippedVersions.push(version);
+ // Still add to versionFiles array so main changelog.ts can import it
+ versionFiles.push({
+ version,
+ fileName,
+ filePath,
+ varName,
+ content: null, // No content needed for existing files
+ skipped: true,
+ });
+ continue;
+ }
+
+ // Generate TypeScript code for this version
+ const entryObjects = versionEntries
+ .map(entry => {
+ const componentsStr = `[${entry.components
+ .map(c => `'${c}'`)
+ .join(', ')}]`;
+ const prsStr = `[${entry.prs.map(pr => `'${pr}'`).join(', ')}]`;
+ const typeStr = entry.type ? `type: '${entry.type}',` : '';
+ const shaStr = entry.commitSha
+ ? `commitSha: '${entry.commitSha}',`
+ : '';
+
+ // Escape description for template literal
+ const descEscaped = entry.description
+ .replace(/\\/g, '\\\\')
+ .replace(/`/g, '\\`')
+ .replace(/\${/g, '\\${');
+
+ return ` {
+ components: ${componentsStr},
+ version: '${entry.version}',
+ prs: ${prsStr},
+ description: \`${descEscaped}\`,
+ ${typeStr}
+ ${shaStr}
+ }`;
+ })
+ .join(',\n');
+
+ const fileContent = `import type { ChangelogProps } from '../types';
+
+export const ${varName}: ChangelogProps[] = [
+${entryObjects}
+];
+`;
+
+ generatedVersions.push(version);
+ versionFiles.push({
+ version,
+ fileName,
+ filePath,
+ varName,
+ content: fileContent,
+ skipped: false,
+ });
+
+ if (!dryRun) {
+ fs.writeFileSync(filePath, fileContent, 'utf-8');
+ }
+ }
+
+ // Log summary
+ if (skippedVersions.length > 0) {
+ console.log(
+ `\n⏭️ Skipped ${
+ skippedVersions.length
+ } existing version files: ${skippedVersions
+ .sort((a, b) => compareVersions(a, b))
+ .join(', ')}`,
+ );
+ }
+ if (generatedVersions.length > 0) {
+ console.log(
+ `\n✨ ${dryRun ? 'Would generate' : 'Generated'} ${
+ generatedVersions.length
+ } new version files: ${generatedVersions
+ .sort((a, b) => compareVersions(a, b))
+ .join(', ')}`,
+ );
+ }
+
+ return versionFiles;
+}
+
+/**
+ * Generate main changelog.ts that imports and spreads all version files
+ */
+async function generateMainChangelog(
+ versionFiles,
+ changelogPath,
+ dryRun = false,
+) {
+ // Get the changelogs directory path
+ const changelogsDir = path.join(path.dirname(changelogPath), 'changelogs');
+
+ // Read all version files from the changelogs directory
+ const allVersionFiles = new Map();
+
+ // First, add all files from the directory (if it exists)
+ if (fs.existsSync(changelogsDir)) {
+ const files = fs.readdirSync(changelogsDir);
+ files.forEach(file => {
+ if (file.match(/^v\d+\.\d+\.\d+\.ts$/)) {
+ const version = file.replace(/^v/, '').replace(/\.ts$/, '');
+ const varName = `changelog_${version.replace(/\./g, '_')}`;
+ allVersionFiles.set(version, {
+ version,
+ fileName: file,
+ varName,
+ });
+ }
+ });
+ }
+
+ // Then, add/update with files from this run (in case new ones were generated)
+ versionFiles.forEach(vf => {
+ allVersionFiles.set(vf.version, {
+ version: vf.version,
+ fileName: vf.fileName,
+ varName: vf.varName,
+ });
+ });
+
+ // Sort versions in descending order
+ const sortedVersions = Array.from(allVersionFiles.values()).sort((a, b) =>
+ compareVersions(b.version, a.version),
+ );
+
+ const imports = sortedVersions
+ .map(
+ v =>
+ `import { ${v.varName} } from './changelogs/${v.fileName.replace(
+ '.ts',
+ '',
+ )}';`,
+ )
+ .join('\n');
+
+ const spreads = sortedVersions.map(v => ` ...${v.varName},`).join('\n');
+
+ const content = `export * from './types';
+${imports}
+
+export const changelog = [
+${spreads}
+];
+`;
+
+ console.log(
+ `\n📝 Main changelog.ts will import all ${sortedVersions.length} version files`,
+ );
+
+ if (!dryRun) {
+ fs.writeFileSync(changelogPath, content, 'utf-8');
+ }
+
+ return content;
+}
+
+/**
+ * Check if an entry already exists in changelog.ts
+ */
+function isDuplicate(entry, existingContent) {
+ // Simple check: look for version + description substring
+ const descStart = entry.description.substring(0, 50);
+ const pattern = `version: '${entry.version}'`;
+
+ const versionIndex = existingContent.indexOf(pattern);
+ if (versionIndex === -1) return false;
+
+ // Check if description appears near this version
+ const contextWindow = existingContent.substring(
+ Math.max(0, versionIndex - 200),
+ versionIndex + 500,
+ );
+
+ return contextWindow.includes(descStart);
+}
+
+/**
+ * Detect GitHub authentication method
+ * Priority: gh CLI → GITHUB_TOKEN env → unauthenticated
+ */
+function getGitHubAuth() {
+ // 1. Try gh CLI (preferred - uses keyring auth)
+ try {
+ // Check specifically for github.com authentication
+ const output = execSync('gh auth status -h github.com 2>&1', {
+ encoding: 'utf-8',
+ });
+ if (output.includes('Logged in to github.com')) {
+ console.log('✓ Using gh CLI authentication');
+ return { method: 'gh-cli' };
+ }
+ } catch {
+ // gh not installed or not authenticated to github.com
+ }
+
+ // 2. Try GITHUB_TOKEN env var
+ if (process.env.GITHUB_TOKEN) {
+ console.log('✓ Using GITHUB_TOKEN authentication');
+ return { method: 'token', token: process.env.GITHUB_TOKEN };
+ }
+
+ // 3. Fallback to unauthenticated (60 req/hour limit)
+ console.warn('⚠️ Using unauthenticated GitHub API (60 requests/hour)');
+ console.warn(' For higher limits: authenticate gh CLI or set GITHUB_TOKEN');
+ return { method: 'unauthenticated' };
+}
+
+// Initialize auth and Octokit
+const githubAuth = getGitHubAuth();
+const octokit =
+ githubAuth.method === 'token'
+ ? new Octokit({ auth: githubAuth.token })
+ : new Octokit();
+
+// Cache for PR lookups
+const prCache = new Map();
+
+/**
+ * Expand a truncated SHA to full SHA using local git repository
+ * Falls back to GitHub API if git rev-parse fails
+ */
+async function expandCommitSha(shortSha) {
+ try {
+ // First try local git repository (fastest, works for all commits)
+ // We need to run this from the backstage monorepo root
+ const repoRoot = path.join(__dirname, '../../');
+
+ try {
+ const fullSha = execSync(`git rev-parse ${shortSha}`, {
+ encoding: 'utf-8',
+ cwd: repoRoot,
+ stdio: ['pipe', 'pipe', 'pipe'],
+ }).trim();
+
+ if (fullSha && fullSha.length === 40) {
+ return fullSha;
+ }
+ } catch (gitError) {
+ // Handle ambiguous SHA - parse git's error output to find commit candidates
+ if (gitError.stderr && gitError.stderr.includes('ambiguous')) {
+ const stderr = gitError.stderr.toString();
+ // Extract commit SHAs from git's hint output
+ // Format: "hint: 83c100e6accfb commit 2025-10-22 - message"
+ const commitMatch = stderr.match(/hint:\s+([a-f0-9]+)\s+commit\s+/);
+ if (commitMatch && commitMatch[1]) {
+ // Found the commit SHA from the ambiguous candidates
+ const candidateSha = commitMatch[1];
+ // Verify this is a valid commit
+ try {
+ const fullSha = execSync(`git rev-parse ${candidateSha}`, {
+ encoding: 'utf-8',
+ cwd: repoRoot,
+ }).trim();
+ if (fullSha && fullSha.length === 40) {
+ return fullSha;
+ }
+ } catch {
+ // Candidate didn't work, continue to GitHub API
+ }
+ }
+ }
+ // Commit not in local repo, try GitHub API
+ }
+ } catch (error) {
+ // Error setting up git command, try GitHub API
+ }
+
+ try {
+ // Fallback to GitHub API
+ if (githubAuth.method === 'gh-cli') {
+ const result = execSync(
+ `gh api repos/backstage/backstage/commits/${shortSha} --jq '.sha' 2>/dev/null`,
+ { encoding: 'utf-8' },
+ ).trim();
+ return result || null;
+ } else {
+ // Use Octokit
+ const { data: commit } = await octokit.rest.repos.getCommit({
+ owner: 'backstage',
+ repo: 'backstage',
+ ref: shortSha,
+ });
+ return commit.sha;
+ }
+ } catch (error) {
+ // Commit not found or other error - return null silently
+ return null;
+ }
+}
+
+/**
+ * Find PR number for a commit SHA using GitHub API
+ */
+async function findPRNumber(commitSha) {
+ // Check cache first
+ if (prCache.has(commitSha)) {
+ return prCache.get(commitSha);
+ }
+
+ try {
+ // If SHA is truncated (7 chars), expand it first
+ let fullSha = commitSha;
+ if (commitSha.length === 7) {
+ fullSha = await expandCommitSha(commitSha);
+ if (!fullSha) {
+ // Commit not found
+ console.warn(`⚠️ Commit not found: ${commitSha}`);
+ prCache.set(commitSha, null);
+ return null;
+ }
+ }
+
+ let prNumber = null;
+
+ // Use gh CLI if available (faster, uses existing auth)
+ if (githubAuth.method === 'gh-cli') {
+ try {
+ const result = execSync(
+ `gh api repos/backstage/backstage/commits/${fullSha}/pulls --jq '.[0].number' 2>/dev/null`,
+ { encoding: 'utf-8' },
+ ).trim();
+ prNumber = result || null;
+ } catch (ghError) {
+ // Handle gh CLI errors gracefully - commit exists but has no PR
+ prNumber = null;
+ }
+ } else {
+ // Otherwise use Octokit (token or unauthenticated)
+ const { data: prs } =
+ await octokit.rest.repos.listPullRequestsAssociatedWithCommit({
+ owner: 'backstage',
+ repo: 'backstage',
+ commit_sha: fullSha,
+ });
+ prNumber = prs.length > 0 ? prs[0].number.toString() : null;
+ }
+
+ // Cache the result
+ prCache.set(commitSha, prNumber);
+ return prNumber;
+ } catch (error) {
+ if (error.message?.includes('rate limit')) {
+ console.error('⚠️ GitHub API rate limit exceeded');
+ console.error(
+ ' Authenticate gh CLI or set GITHUB_TOKEN for higher limits',
+ );
+ } else {
+ console.warn(`⚠️ Error finding PR for ${commitSha}: ${error.message}`);
+ }
+ prCache.set(commitSha, null);
+ return null;
+ }
+}
+
+/**
+ * Main sync function
+ */
+async function main() {
+ const args = process.argv.slice(2);
+ const dryRun = args.includes('--dry-run');
+
+ const changelogTsPath = path.join(__dirname, '../src/utils/changelog.ts');
+ const changelogMdPath = path.join(
+ __dirname,
+ '../../packages/ui/CHANGELOG.md',
+ );
+
+ // Validate files exist
+ if (!fs.existsSync(changelogTsPath)) {
+ throw new Error(`changelog.ts not found at: ${changelogTsPath}`);
+ }
+ if (!fs.existsSync(changelogMdPath)) {
+ throw new Error(`CHANGELOG.md not found at: ${changelogMdPath}`);
+ }
+
+ console.log('📋 Syncing UI component changelogs...\n');
+
+ // Get last synced version
+ const lastVersion = getLastSyncedVersion(changelogTsPath);
+ console.log(`Last synced version: ${lastVersion || '(none)'}`);
+
+ // Get valid components
+ const validComponents = getValidComponents(changelogTsPath);
+ console.log(`Valid components: ${validComponents.length}`);
+
+ // Parse CHANGELOG.md
+ console.log('\n📖 Parsing CHANGELOG.md...');
+ const allEntries = await parseChangelogMd(
+ changelogMdPath,
+ lastVersion,
+ validComponents,
+ );
+ console.log(`Found ${allEntries.length} total entries since ${lastVersion}`);
+
+ // Read existing changelog content for duplicate detection
+ const existingContent = fs.readFileSync(changelogTsPath, 'utf-8');
+
+ // Filter to only new, non-duplicate entries
+ const relevantEntries = allEntries.filter(e => {
+ const hasComponents = e.components.length > 0 || e.components.length === 0;
+ const notDuplicate = !isDuplicate(e, existingContent);
+ return hasComponents && notDuplicate;
+ });
+
+ const duplicatesCount = allEntries.length - relevantEntries.length;
+ if (duplicatesCount > 0) {
+ console.log(`Skipped ${duplicatesCount} duplicate entries`);
+ }
+
+ console.log(
+ `Relevant entries (with or without components): ${relevantEntries.length}`,
+ );
+
+ if (relevantEntries.length === 0) {
+ console.log('\n✅ No new entries to sync');
+ return;
+ }
+
+ // Fetch PR numbers for new entries (lazy fetch - only for entries that will be written)
+ await fetchPRNumbers(relevantEntries, dryRun);
+
+ // Show summary
+ console.log('\n📝 New entries by component:');
+ const byComponent = {};
+ relevantEntries.forEach(entry => {
+ if (entry.components.length === 0) {
+ byComponent['(general)'] = (byComponent['(general)'] || 0) + 1;
+ } else {
+ entry.components.forEach(comp => {
+ byComponent[comp] = (byComponent[comp] || 0) + 1;
+ });
+ }
+ });
+ Object.entries(byComponent).forEach(([comp, count]) => {
+ console.log(` - ${comp}: ${count} ${count === 1 ? 'entry' : 'entries'}`);
+ });
+
+ // Warn about unknown components
+ const unknownComponents = [];
+ allEntries.forEach(entry => {
+ const fullText = entry.description;
+ const componentMatch = fullText.match(
+ /Affected components?:[ \t]*([^\n]+)/i,
+ );
+ if (componentMatch) {
+ const names = componentMatch[1].split(',').map(n => n.trim());
+ names.forEach(name => {
+ if (!mapComponentName(name, validComponents)) {
+ unknownComponents.push(name);
+ }
+ });
+ }
+ });
+
+ if (unknownComponents.length > 0) {
+ console.log('\n⚠️ Unknown components (skipped):');
+ [...new Set(unknownComponents)].forEach(name => {
+ console.log(` - ${name}`);
+ });
+ }
+
+ // Create changelogs directory if it doesn't exist
+ const changelogsDir = path.join(__dirname, '../src/utils/changelogs');
+ if (!fs.existsSync(changelogsDir) && !dryRun) {
+ fs.mkdirSync(changelogsDir, { recursive: true });
+ }
+
+ // Generate version files
+ console.log(
+ `\n${
+ dryRun ? '🔍 Dry run - would generate' : '✍️ Generating'
+ } version files...`,
+ );
+
+ const versionFiles = await generateVersionFiles(
+ relevantEntries,
+ changelogsDir,
+ dryRun,
+ );
+
+ // Generate main changelog.ts
+ const mainContent = await generateMainChangelog(
+ versionFiles,
+ changelogTsPath,
+ dryRun,
+ );
+
+ if (!dryRun) {
+ console.log('\n✅ Changelog sync complete!');
+ const newFiles = versionFiles.filter(vf => !vf.skipped).length;
+ const skippedFiles = versionFiles.filter(vf => vf.skipped).length;
+ if (newFiles > 0) {
+ console.log(
+ ` Generated ${newFiles} new version file${newFiles === 1 ? '' : 's'}`,
+ );
+ }
+ if (skippedFiles > 0) {
+ console.log(
+ ` Preserved ${skippedFiles} existing version file${
+ skippedFiles === 1 ? '' : 's'
+ }`,
+ );
+ }
+ console.log(` Updated ${changelogTsPath}`);
+ } else {
+ console.log('\n📄 Sample version file content:\n');
+ // Find first non-skipped version file for sample
+ const sampleFile = versionFiles.find(vf => !vf.skipped && vf.content);
+ if (sampleFile) {
+ const sample = sampleFile.content.split('\n').slice(0, 30).join('\n');
+ console.log(sample);
+ console.log('...\n');
+ } else {
+ console.log('(No new version files to generate)\n');
+ }
+
+ console.log('\n📄 Main changelog.ts content:\n');
+ console.log(mainContent);
+
+ const newFiles = versionFiles.filter(vf => !vf.skipped).length;
+ const skippedFiles = versionFiles.filter(vf => vf.skipped).length;
+
+ console.log('\n✅ Dry run complete - no files were modified');
+ if (newFiles > 0) {
+ console.log(
+ ` Run without --dry-run to generate ${newFiles} new version file${
+ newFiles === 1 ? '' : 's'
+ }`,
+ );
+ }
+ if (skippedFiles > 0) {
+ console.log(
+ ` ${skippedFiles} existing version file${
+ skippedFiles === 1 ? '' : 's'
+ } will be preserved`,
+ );
+ }
+ }
+}
+
+// Run main if executed directly
+if (import.meta.url === `file://${process.argv[1]}`) {
+ main().catch(error => {
+ console.error('❌ Error:', error.message);
+ process.exit(1);
+ });
+}
diff --git a/docs-ui/src/app/about/page.mdx b/docs-ui/src/app/about/page.mdx
deleted file mode 100644
index 2c87a3d03e..0000000000
--- a/docs-ui/src/app/about/page.mdx
+++ /dev/null
@@ -1,32 +0,0 @@
-# About Backstage UI
-
-Backstage UI is a design system created specifically for Backstage, built with React, TypeScript, and vanilla CSS.
-This open-source library is hosted in the Backstage monorepo. While it can be used in other projects, Backstage UI
-is designed to deliver a consistent, accessible, and extensible experience tailored to Backstage users.
-
-## Philosophy
-
-Backstage empowers product teams to build software faster and with greater quality. Its extensibility,
-however, required us to rethink how to deliver a consistent and accessible user experience. Our goal is
-to enable plugin creators to design plugins that seamlessly integrate with Backstage's look and feel while
-still allowing customization to match individual brands.
-
-Instead of reinventing the wheel, we chose to focus on layout and styling while leveraging existing headless
-component libraries for functionality. This approach allows us to dedicate our efforts to creating a cohesive
-and flexible theming system.
-
-## Team
-
-Backstage UI is designed and maintained primarily by Spotify's Backstage team, leveraging Spotify's expertise in
-crafting high-quality design and technology. Drawing from our experience in building reliable and intuitive
-user experiences for the music industry, we've created a design system that looks great and works seamlessly.
-
-## Community
-
-Backstage UI is an open-source project and we welcome contributions from the community. If you are interested in
-contributing to Backstage UI, please read our [contributing guide](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md)
-and our [code of conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md).
-
-## License
-
-Backstage UI is licensed under the Apache 2.0 license. See the [LICENSE](https://github.com/backstage/backstage/blob/master/LICENSE) file for more details.
diff --git a/docs-ui/src/app/components/[slug]/page.tsx b/docs-ui/src/app/components/[slug]/page.tsx
index d6f98c8f93..1422334471 100644
--- a/docs-ui/src/app/components/[slug]/page.tsx
+++ b/docs-ui/src/app/components/[slug]/page.tsx
@@ -7,9 +7,7 @@ export default async function Page({
}) {
const { slug } = await params;
- const { default: Component } = await import(
- `@/content/components/${slug}.mdx`
- );
+ const { default: Component } = await import(`@/content/${slug}.mdx`);
return ;
}
diff --git a/docs-ui/src/app/components/page.mdx b/docs-ui/src/app/components/page.mdx
new file mode 100644
index 0000000000..bd18005d19
--- /dev/null
+++ b/docs-ui/src/app/components/page.mdx
@@ -0,0 +1,173 @@
+import { ComponentCards, ComponentCard } from '@/components/ComponentCards';
+import { LayoutComponents } from '@/components/LayoutComponents';
+import { CodeBlock } from '@/components/CodeBlock';
+
+# Components
+
+## Layout Components
+
+We built a couple of layout components to help you build responsive elements
+that will be consistent with the rest of your Backstage instance. These
+components are opinionated and use TypeScript to ensure that the props you
+provide are the ones coming from the theme.
+
+
+ Hello World
+
+ Project 1
+ Project 2
+
+
+`}
+/>
+
+
+
+## Components
+
+### Actions
+
+
+
+
+
+
+
+
+### Content display
+
+
+
+
+
+
+### Selection and inputs
+
+
+
+
+
+
+
+
+
+
+
+### Navigation
+
+
+
+
+
+
+
+
+### Images and icons
+
+
+
+
+
+
+### Feedback indicators
+
+
+
+
+
+
+### Typography
+
+
+
+
diff --git a/docs-ui/src/app/install/page.mdx b/docs-ui/src/app/install/page.mdx
deleted file mode 100644
index cf14691441..0000000000
--- a/docs-ui/src/app/install/page.mdx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { CodeBlock } from '@/components/CodeBlock';
-import { Banner } from '@/components/Banner';
-import { snippet } from './snippets';
-
-# How to install Backstage UI
-
-## How to import BUI's global styles
-
-Backstage UI works by importing a global CSS file at the root of your application. This file includes all the default styles for the components.
-First, you'll need to install the package using a package manager. For example, if you're using Yarn:
-
-
-
-);`}
-/>
-
-
-
-## How to use BUI components
-
-As a plugin maintainer, you can use BUI components in your plugin. As mentioned above, you should not import the styles
-again in your plugin as this will be handled at the root of your application. To get started, just add the library to
-your plugin and import the components you need.
-
-
-
-
-
-## Next steps
-
-Now that you have the basics down, you can start building your plugin using the new design system.
-Please familiarise yourself first with our theming principles. This will help you understand the core concepts of the design system.
-If you have any questions, please reach out to us on [Discord](https://discord.gg/MUpMjP2).
diff --git a/docs-ui/src/app/layout.module.css b/docs-ui/src/app/layout.module.css
index 7399fc2485..df89f02283 100644
--- a/docs-ui/src/app/layout.module.css
+++ b/docs-ui/src/app/layout.module.css
@@ -1,28 +1,46 @@
.container {
position: relative;
z-index: 20;
- width: calc(100% - 64px);
- margin-inline: 0 16px;
- margin-left: 32px;
- margin-right: 32px;
- margin-top: 112px;
margin-bottom: 48px;
- padding-inline: 40px;
- padding-block: 48px;
- background-color: var(--panel);
- border-radius: 0.5rem;
+ padding-inline: 24px;
+}
+
+.contentWrapper {
+ display: flex;
+ width: 100%;
+ max-width: 1200px;
+ margin: 0 auto;
+ justify-content: center;
+ flex-direction: column;
}
.content {
- width: 100%;
- max-width: 960px;
- margin: 0 auto;
+ display: flex;
+ gap: 40px;
+}
+
+.contentInner {
+ flex: 1;
+ min-width: 0;
+ overflow-x: hidden;
+}
+
+.toc {
+ display: none;
}
@media (min-width: 768px) {
.container {
- width: calc(100% - 332px - 40px);
- margin-left: 332px;
+ width: calc(100% - 260px);
+ margin-left: 260px;
margin-right: 40px;
}
}
+
+@media (min-width: 1280px) {
+ .toc {
+ display: block;
+ width: 240px;
+ flex-shrink: 0;
+ }
+}
diff --git a/docs-ui/src/app/layout.tsx b/docs-ui/src/app/layout.tsx
index d197112431..ad056626d8 100644
--- a/docs-ui/src/app/layout.tsx
+++ b/docs-ui/src/app/layout.tsx
@@ -1,9 +1,10 @@
import type { Metadata } from 'next';
-import { Sidebar } from '../components/Sidebar';
+import { Sidebar } from '@/components/Sidebar/Sidebar';
import { Toolbar } from '@/components/Toolbar';
-import { StickyHeader } from '../components/StickyHeader/StickyHeader';
import { Providers } from './providers';
import { CustomTheme } from '@/components/CustomTheme';
+import { TableOfContents } from '@/components/TableOfContents';
+import { MobileBottomNav } from '@/components/MobileBottomNav';
import styles from './layout.module.css';
import '../css/globals.css';
@@ -51,11 +52,18 @@ export default async function RootLayout({
-
-
-
{children}
+
+
+
+
{children}
+
+
+
+
diff --git a/docs-ui/src/app/layout/page.mdx b/docs-ui/src/app/layout/page.mdx
deleted file mode 100644
index 3c3b998490..0000000000
--- a/docs-ui/src/app/layout/page.mdx
+++ /dev/null
@@ -1,42 +0,0 @@
-import { LayoutComponents } from '@/components/LayoutComponents';
-import { CodeBlock } from '@/components/CodeBlock';
-
-# Layout
-
-Backstage UI is made for extensibility. We built this library to make it easy for any
-Backstage plugin creator to be able to build their ideas at speed ensuring
-consistency across the rest of your ecosystem. Each component is designed to
-be editable to match your need but sometimes you want to have more control
-over the layout of your page. To help you with that, we created a set of
-layout components that you can use to build your own layouts. All of these
-components are built to extend on our theming system, making it easy for you
-to build your own layouts. Sometimes these components are not enough so we
-created a set of helpers to be used with any CSS-in-JS library.
-
-## Layout Components
-
-We built a couple of layout components to help you build responsive elements
-that will be consistent with the rest of your Backstage instance. These
-components are opinionated and use TypeScript to ensure that the props you
-provide are the ones coming from the theme.
-
-
- Hello World
-
- Project 1
- Project 2
-
-
-`}
-/>
-
-
-
-## Layout Helpers
-
-Sometimes you want to use global tokens dynamically outside of React
-components. To help you with that we would like to provide a set of helpers
-that you can use in your code. These helpers are not available just yet but we
-are working on it.
diff --git a/docs-ui/src/app/page.mdx b/docs-ui/src/app/page.mdx
index ffd3bc9dcb..9a796b401b 100644
--- a/docs-ui/src/app/page.mdx
+++ b/docs-ui/src/app/page.mdx
@@ -1,179 +1,83 @@
-import { ComponentCards, ComponentCard } from '@/components/ComponentCards';
+import { CodeBlock } from '@/components/CodeBlock';
+import { Banner } from '@/components/Banner';
+import { snippet } from './snippets';
-## Welcome to Backstage UI, the new design library for Backstage plugins.
+# Welcome to Backstage UI
-This project is still under active development but we will make sure to document
-the API as we go. We are aiming to improve the general UI of Backstage and
-plugins across Backstage. This new library will take time to build but we are
-building it incrementally with not conflict with the existing theming system.
+Backstage UI is a design system created specifically for Backstage, built with React, TypeScript, and vanilla CSS.
+This open-source library is hosted in the Backstage monorepo. While it can be used in other projects, Backstage UI
+is designed to deliver a consistent, accessible, and extensible experience tailored to Backstage users.
-### Actions
+## Import BUI's global styles
-
-
-
-
-
-
+Backstage UI works by importing a global CSS file at the root of your application. This file includes all the default styles for the components.
+First, you'll need to install the package using a package manager. For example, if you're using Yarn:
-### Layout
+
-
-
-
-
-
-
-
-
+);`}
+/>
-
-
-
-
-
-
-
-
-
+
-### Navigation
+## Use BUI components
-
-
-
-
-
-
+As a plugin maintainer, you can use BUI components in your plugin. As mentioned above, you should not import the styles
+again in your plugin as this will be handled at the root of your application. To get started, just add the library to
+your plugin and import the components you need.
-### Images and icons
+
-
-
-
-
-
-### Feedback indicators
-
-
-
-
-
-
-### Typography
-
-
-
-
+
## Support
Now that you have the basics down, you can start building your plugin using the new design system.
Please familiarise yourself first with our theming principles. This will help you understand the core concepts of the design system.
If you have any questions, please reach out to us on [Discord](https://discord.gg/MUpMjP2).
+
+## Philosophy
+
+Backstage empowers product teams to build software faster and with greater quality. Its extensibility,
+however, required us to rethink how to deliver a consistent and accessible user experience. Our goal is
+to enable plugin creators to design plugins that seamlessly integrate with Backstage's look and feel while
+still allowing customization to match individual brands.
+
+Instead of reinventing the wheel, we chose to focus on layout and styling while leveraging existing headless
+component libraries for functionality. This approach allows us to dedicate our efforts to creating a cohesive
+and flexible theming system.
+
+## Team
+
+Backstage UI is designed and maintained primarily by Spotify's Backstage team, leveraging Spotify's expertise in
+crafting high-quality design and technology. Drawing from our experience in building reliable and intuitive
+user experiences for the music industry, we've created a design system that looks great and works seamlessly.
+
+## Community
+
+Backstage UI is an open-source project and we welcome contributions from the community. If you are interested in
+contributing to Backstage UI, please read our [contributing guide](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md)
+and our [code of conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md).
+
+## License
+
+Backstage UI is licensed under the Apache 2.0 license. See the [LICENSE](https://github.com/backstage/backstage/blob/master/LICENSE) file for more details.
diff --git a/docs-ui/src/app/responsive/page.mdx b/docs-ui/src/app/responsive/page.mdx
deleted file mode 100644
index 9d37b1329f..0000000000
--- a/docs-ui/src/app/responsive/page.mdx
+++ /dev/null
@@ -1,107 +0,0 @@
-import * as Table from '@/components/Table';
-import { Chip } from '@/components/Chip';
-import { CodeBlock } from '@/components/CodeBlock';
-
-# Responsive
-
-Backstage UI is built on a responsive design system, meaning that the components are
-designed to adapt to different screen sizes. By default we offer a set of
-breakpoints that you can use to create responsive components.
-
-## Breakpoints
-
-
-
-
- Breakpoint prefix
- Minimum width
- CSS
-
-
-
-
-
- xs
-
-
- 0px
-
-
- {`{ ... }`}
-
-
-
-
- sm
-
-
- 640px
-
-
- {`@media (min-width: 640px) { ... }`}
-
-
-
-
- md
-
-
- 768px
-
-
- {`@media (min-width: 768px) { ... }`}
-
-
-
-
- lg
-
-
- 1024px
-
-
- {`@media (min-width: 1024px) { ... }`}
-
-
-
-
- xl
-
-
- 1280px
-
-
- {`@media (min-width: 1280px) { ... }`}
-
-
-
-
- 2xl
-
-
- 1536px
-
-
- {`@media (min-width: 1536px) { ... }`}
-
-
-
-
-
-## Responsive components
-
-Backstage UI components are designed to be responsive, meaning that they will adapt
-to different screen sizes. Not every component is responsive, but the ones
-that are will have a prop to control the responsive behavior.
-
-The behaviour is the same for each component. For each prop, instead of adding
-the value, you add an object with the value and the breakpoint prefix.
-
-Button
-
-// Responsive value
-
-`} />
diff --git a/docs-ui/src/app/install/snippets.ts b/docs-ui/src/app/snippets.ts
similarity index 100%
rename from docs-ui/src/app/install/snippets.ts
rename to docs-ui/src/app/snippets.ts
diff --git a/docs-ui/src/app/theming/page.mdx b/docs-ui/src/app/tokens/page.mdx
similarity index 86%
rename from docs-ui/src/app/theming/page.mdx
rename to docs-ui/src/app/tokens/page.mdx
index 455b2cd7f5..8e9031c81c 100644
--- a/docs-ui/src/app/theming/page.mdx
+++ b/docs-ui/src/app/tokens/page.mdx
@@ -3,48 +3,109 @@ import * as Table from '@/components/Table';
import { Chip } from '@/components/Chip';
import { customTheme } from '@/snippets/code-snippets';
-# Theming
+# Tokens
-Backstage UI theming is built entirely on CSS, without relying on any CSS-in-JS libraries.
-At its core, it provides a solid default theme that is easily customizable using a
-comprehensive set of CSS variables. Additionally, it enables anyone to adapt the design
-to their specific needs. Each component comes with fixed class names, making customization
-even more straightforward.
+## Responsive breakpoints
-## Light & Dark modes
+Backstage UI is built on a responsive design system, meaning that the components are
+designed to adapt to different screen sizes. By default we offer a set of
+breakpoints that you can use to create responsive components.
-By default, Backstage UI supports both light and dark modes using the `data-theme-mode` attribute.
-The light theme is applied by default if no `data-theme-mode` attribute is specified. To create
-a custom theme, you'll need to define both light and dark modes as outlined below. If
-only one mode is defined, the other will fall back to the default theme.
+
+
+
+ Breakpoint prefix
+ Minimum width
+ CSS
+
+
+
+
+
+ xs
+
+
+ 0px
+
+
+ {`{ ... }`}
+
+
+
+
+ sm
+
+
+ 640px
+
+
+ {`@media (min-width: 640px) { ... }`}
+
+
+
+
+ md
+
+
+ 768px
+
+
+ {`@media (min-width: 768px) { ... }`}
+
+
+
+
+ lg
+
+
+ 1024px
+
+
+ {`@media (min-width: 1024px) { ... }`}
+
+
+
+
+ xl
+
+
+ 1280px
+
+
+ {`@media (min-width: 1280px) { ... }`}
+
+
+
+
+ 2xl
+
+
+ 1536px
+
+
+ {`@media (min-width: 1536px) { ... }`}
+
+
+
+
-## How to create your own theme
+Backstage UI components are designed to be responsive, meaning that they will adapt
+to different screen sizes. Not every component is responsive, but the ones
+that are will have a prop to control the responsive behavior.
-In our [started guide](/), we ask you to import two css files. The `core.css` file includes
-the default set of variables. We recommend to keep this file in place and add your own theme
-on top of it. `core.css` also include an opinionated reset. If you decided to remove `core.css`
-you will have to provide your own reset css.
+The behaviour is the same for each component. For each prop, instead of adding
+the value, you add an object with the value and the breakpoint prefix.
-Here's an example of how your theme.css file should look like:
+
+
-## CSS class name structure
+// Responsive value
-All Backstage UI components come with a set of CSS classes that you can use to style them. To make it
-easier to identify the class name you can use, we use a specific structure for the class names.
+`} />
-
-
-Every component has a unique prefix `.bui-` followed by the component name. Component props
-are represented using the `data-` attribute. That way, class names are easily identifiable.
-
-## Available CSS variables
-
-### Base colors
+## Base colors
These colors are used for special purposes like ring, scrollbar, ...
@@ -123,7 +184,7 @@ These colors are used for special purposes like ring, scrollbar, ...
-### Core background colors
+## Core background colors
These colors are used for the background of your application. We are mostly using for now a
single elevated background for panels. `--bui-bg` should mostly use as the main background
@@ -224,7 +285,7 @@ color of your app.
-### Foreground colors
+## Foreground colors
Foreground colours are meant to work in pair with a background colours. Typically this would work
for icons, texts, shapes, ... Use a matching name to know what foreground color to use. These colors
@@ -329,7 +390,7 @@ are prefixed with `fg` to make it easier to identify.
-### Border colors
+## Border colors
These border colors are mostly meant to be used as borders on top of any components with
low contrast to help as a separator with the different background colors.
@@ -391,7 +452,7 @@ low contrast to help as a separator with the different background colors.
-### Special colors
+## Special colors
These colors are used for special purposes like ring, scrollbar, ...
@@ -424,7 +485,7 @@ These colors are used for special purposes like ring, scrollbar, ...
-### Font families
+## Font families
We have two fonts that we use across Backstage UI. The first one is the sans-serif
font that we use for the body of the application. The second one is the
@@ -453,7 +514,7 @@ monospace font that we use for code blocks and tables.
-### Font weights
+## Font weights
We have two font weights that we use across Backstage UI. Regular or Bold.
@@ -480,7 +541,7 @@ We have two font weights that we use across Backstage UI. Regular or Bold.
-### Spacing
+## Spacing
We built a spacing system based on a single value `--bui-space`. This value is
used to calculate the spacing for all the components. By default if you would like to
@@ -619,7 +680,7 @@ tokens for pretty much each spacing properties like padding, margin, gaps, ...
-### Radius
+## Radius
We use a radius system to make sure that the components have a consistent look and feel.
diff --git a/docs-ui/src/components/Changelog/index.tsx b/docs-ui/src/components/Changelog/index.tsx
index 224e23d668..d7a547b901 100644
--- a/docs-ui/src/components/Changelog/index.tsx
+++ b/docs-ui/src/components/Changelog/index.tsx
@@ -52,14 +52,15 @@ export function Changelog() {
${bumpEntries
.map(e => {
const prs =
- e.prs.length > 0 &&
- e.prs
- .map(
- pr =>
- `[#${pr}](https://github.com/backstage/backstage/pull/${pr})`,
- )
- .join(', ');
- return `- ${e.description} ${prs}`;
+ e.prs.length > 0
+ ? e.prs
+ .map(
+ pr =>
+ `[#${pr}](https://github.com/backstage/backstage/pull/${pr})`,
+ )
+ .join(', ')
+ : '';
+ return `- ${e.description}${prs ? ` ${prs}` : ''}`;
})
.join('\n')}`;
})
diff --git a/docs-ui/src/components/ChangelogComponent/index.tsx b/docs-ui/src/components/ChangelogComponent/index.tsx
index f8832b0731..ff86433268 100644
--- a/docs-ui/src/components/ChangelogComponent/index.tsx
+++ b/docs-ui/src/components/ChangelogComponent/index.tsx
@@ -17,14 +17,17 @@ export const ChangelogComponent = ({ component }: { component: Component }) => {
${componentChangelog
?.map(change => {
const prs =
- change.prs.length > 0 &&
- change.prs
- .map(
- pr =>
- `[#${pr}](https://github.com/backstage/backstage/pull/${pr})`,
- )
- .join(', ');
- return `- \`${change.version}\` - ${change.description} ${prs}`;
+ change.prs.length > 0
+ ? change.prs
+ .map(
+ pr =>
+ `[#${pr}](https://github.com/backstage/backstage/pull/${pr})`,
+ )
+ .join(', ')
+ : '';
+ return `- \`${change.version}\` - ${change.description}${
+ prs ? ` ${prs}` : ''
+ }`;
})
.join('\n')}`}
/>
diff --git a/docs-ui/src/components/CodeBlock/index.tsx b/docs-ui/src/components/CodeBlock/index.tsx
index 96fdb6bea5..1467bca465 100644
--- a/docs-ui/src/components/CodeBlock/index.tsx
+++ b/docs-ui/src/components/CodeBlock/index.tsx
@@ -9,15 +9,24 @@ export interface CodeBlockProps {
code?: string;
}
-export async function CodeBlock({ lang = 'tsx', title, code }: CodeBlockProps) {
- const out = await codeToHtml(code || '', {
- lang: lang,
+export async function CodeBlock(props: CodeBlockProps) {
+ const { lang = 'tsx', title, code } = props;
+ let out = await codeToHtml(code || '', {
+ lang,
+ transformers: [transformerNotationDiff({ matchAlgorithm: 'v3' })],
themes: {
- light: 'min-light',
+ light: 'github-dark',
dark: 'min-dark',
},
- transformers: [transformerNotationDiff({ matchAlgorithm: 'v3' })],
});
+ // Remove background-color from the pre tag to use our theme colors
+ out = out.replace(
+ /style="([^"]*?)background-color:[^;]+;?([^"]*?)"/g,
+ 'style="$1$2"',
+ );
+ // Clean up empty style attributes
+ out = out.replace(/style=""\s?/g, '');
+
return ;
}
diff --git a/docs-ui/src/components/CodeBlock/styles.module.css b/docs-ui/src/components/CodeBlock/styles.module.css
index d786140482..30e1837f60 100644
--- a/docs-ui/src/components/CodeBlock/styles.module.css
+++ b/docs-ui/src/components/CodeBlock/styles.module.css
@@ -1,33 +1,29 @@
.codeBlock {
- border-radius: 4px;
- border: 1px solid var(--border);
+ border-radius: 8px;
position: relative;
background: transparent;
overflow-x: auto;
font-family: var(--font-mono);
- background-color: #fff;
+ background-color: var(--code-bg);
margin-bottom: 1rem;
}
-[data-theme-mode='dark'] .codeBlock {
- background-color: #121212;
-}
-
.title {
- border-bottom: 1px solid var(--border);
padding: 12px 20px;
font-size: 0.8125rem;
- color: var(--primary);
+ color: #fff;
font-family: var(--font-regular);
font-weight: var(--font-weight-bold);
+ background-color: var(--code-title);
}
.title code {
- background-color: var(--surface-1);
+ background-color: rgba(255, 255, 255, 0.1);
padding: 0.2rem 0.375rem;
border-radius: 0.25rem;
- color: var(--secondary);
- font-size: 0.8125rem;
+ color: #fff;
+ font-size: 0.75rem;
+ font-weight: var(--font-weight-bold);
}
.code {
diff --git a/docs-ui/src/components/ComponentCards/ComponentCards.module.css b/docs-ui/src/components/ComponentCards/ComponentCards.module.css
index 688010110c..8eb9dbd3d0 100644
--- a/docs-ui/src/components/ComponentCards/ComponentCards.module.css
+++ b/docs-ui/src/components/ComponentCards/ComponentCards.module.css
@@ -28,7 +28,7 @@
display: flex;
flex-direction: column;
justify-content: flex-end;
- background-color: var(--panel);
+ background-color: var(--bg);
border-radius: 8px;
border: 1px solid var(--border);
padding: 16px;
@@ -36,7 +36,7 @@
min-height: 120px;
&:hover {
- background-color: var(--panel-hover);
+ background-color: var(--bg-hover);
}
}
diff --git a/docs-ui/src/components/CustomTheme/styles.module.css b/docs-ui/src/components/CustomTheme/styles.module.css
index 05604c48be..dddd3ff7e4 100644
--- a/docs-ui/src/components/CustomTheme/styles.module.css
+++ b/docs-ui/src/components/CustomTheme/styles.module.css
@@ -5,7 +5,7 @@
right: 16px;
width: 240px;
height: 47px;
- background-color: var(--panel);
+ background-color: var(--bg);
border-radius: 0.375rem;
border: 1px solid var(--border);
display: flex;
@@ -33,7 +33,7 @@
height: 46px;
flex-shrink: 0;
border-bottom: 1px solid var(--border);
- background-color: var(--panel);
+ background-color: var(--bg);
display: flex;
justify-content: space-between;
align-items: center;
diff --git a/docs-ui/src/components/HeadlessBanners/styles.module.css b/docs-ui/src/components/HeadlessBanners/styles.module.css
index 384327c394..c0066eccff 100644
--- a/docs-ui/src/components/HeadlessBanners/styles.module.css
+++ b/docs-ui/src/components/HeadlessBanners/styles.module.css
@@ -1,6 +1,6 @@
.container {
display: flex;
- background-color: var(--panel);
+ background-color: var(--bg);
padding: 1.5rem;
border-radius: 0.25rem;
border: 1px solid var(--border);
diff --git a/docs-ui/src/components/LayoutComponents/LayoutComponents.module.css b/docs-ui/src/components/LayoutComponents/LayoutComponents.module.css
index a09a04fb5c..9009cdb6a4 100644
--- a/docs-ui/src/components/LayoutComponents/LayoutComponents.module.css
+++ b/docs-ui/src/components/LayoutComponents/LayoutComponents.module.css
@@ -19,7 +19,7 @@
& .content {
flex: none;
- background-color: var(--panel);
+ background-color: var(--bg);
border: 1px solid var(--border);
border-radius: 4px;
width: 100%;
diff --git a/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.module.css b/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.module.css
new file mode 100644
index 0000000000..632f005b65
--- /dev/null
+++ b/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.module.css
@@ -0,0 +1,40 @@
+.icon {
+ display: block;
+}
+
+.topLine,
+.middleLine,
+.bottomLine {
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+/* Menu state (default) */
+.topLine {
+ transform-origin: 10px 5px;
+ transform: rotate(0deg);
+}
+
+.middleLine {
+ opacity: 1;
+ transform-origin: 10px 10px;
+}
+
+.bottomLine {
+ transform-origin: 10px 15px;
+ transform: rotate(0deg);
+}
+
+/* Close state (when open) */
+.icon[data-open='true'] .topLine {
+ transform-origin: 10px 5px;
+ transform: translateY(5px) rotate(45deg);
+}
+
+.icon[data-open='true'] .middleLine {
+ opacity: 0;
+}
+
+.icon[data-open='true'] .bottomLine {
+ transform-origin: 10px 15px;
+ transform: translateY(-5px) rotate(-45deg);
+}
diff --git a/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.tsx b/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.tsx
new file mode 100644
index 0000000000..80edd64d43
--- /dev/null
+++ b/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.tsx
@@ -0,0 +1,56 @@
+import styles from './AnimatedMenuIcon.module.css';
+
+interface AnimatedMenuIconProps {
+ isOpen: boolean;
+ size?: number;
+}
+
+export const AnimatedMenuIcon = ({
+ isOpen,
+ size = 20,
+}: AnimatedMenuIconProps) => {
+ return (
+
+ );
+};
diff --git a/docs-ui/src/components/MobileBottomNav/MobileBottomNav.module.css b/docs-ui/src/components/MobileBottomNav/MobileBottomNav.module.css
new file mode 100644
index 0000000000..9c128c3959
--- /dev/null
+++ b/docs-ui/src/components/MobileBottomNav/MobileBottomNav.module.css
@@ -0,0 +1,205 @@
+/* Mobile Bottom Navigation Island */
+.mobileBottomNav {
+ display: block;
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ padding: 16px;
+ z-index: 300;
+ pointer-events: none;
+
+ @media (min-width: 768px) {
+ display: none;
+ }
+}
+
+.island {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ margin: 0 auto;
+ width: 50vw;
+ padding: 8px 12px 8px 12px;
+ border-radius: 32px;
+ pointer-events: auto;
+ background-color: #000;
+}
+
+:global([data-theme-mode='dark']) .island {
+ background-color: #fff;
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.3);
+}
+
+.menuButton {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 8px;
+ border-radius: 50%;
+ transition: background-color 0.2s ease-in-out;
+
+ /* White icons on light mode (black island) */
+ color: white;
+
+ &:hover {
+ background-color: rgba(255, 255, 255, 0.1);
+ }
+}
+
+:global([data-theme-mode='dark']) .menuButton {
+ /* Black icons on dark mode (white island) */
+ color: black;
+
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.1);
+ }
+}
+
+.buttonGroup {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ padding: 4px;
+ border-radius: 24px;
+ background-color: rgba(255, 255, 255, 0.2);
+}
+
+:global([data-theme-mode='dark']) .buttonGroup {
+ background-color: rgba(0, 0, 0, 0.08);
+}
+
+.buttonGroup button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 6px 12px;
+ border-radius: 20px;
+ transition: background-color 0.2s ease-in-out;
+
+ /* White icons on light mode */
+ color: white;
+
+ &[data-selected] {
+ background-color: rgba(255, 255, 255, 0.2);
+ }
+
+ &:hover {
+ background-color: rgba(255, 255, 255, 0.15);
+ }
+}
+
+:global([data-theme-mode='dark']) .buttonGroup button {
+ /* Black icons on dark mode */
+ color: black;
+
+ &[data-selected] {
+ background-color: rgba(0, 0, 0, 0.2);
+ }
+
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.15);
+ }
+}
+
+/* Mobile Menu Bottom Sheet */
+.overlay {
+ position: fixed;
+ inset: 0;
+ z-index: 200;
+ background-color: rgba(38, 38, 38, 0.7);
+ display: flex;
+ align-items: flex-end;
+ justify-content: center;
+
+ &[data-entering] {
+ animation: overlayFadeIn 200ms ease-out;
+ }
+
+ &[data-exiting] {
+ animation: overlayFadeOut 200ms ease-in;
+ }
+}
+
+@keyframes overlayFadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+@keyframes overlayFadeOut {
+ from {
+ opacity: 1;
+ }
+ to {
+ opacity: 0;
+ }
+}
+
+.modal {
+ width: calc(100% - 48px);
+ max-height: 50vh;
+ background-color: var(--bg);
+ border-radius: 16px;
+ box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.2);
+ display: flex;
+ flex-direction: column;
+ outline: none;
+ margin-bottom: 96px;
+
+ &[data-entering] {
+ animation: slideUp 300ms cubic-bezier(0.32, 0.72, 0, 1);
+ }
+
+ &[data-exiting] {
+ animation: slideDown 200ms cubic-bezier(0.32, 0.72, 0, 1);
+ }
+}
+
+@keyframes slideUp {
+ from {
+ transform: translateY(8%);
+ }
+ to {
+ transform: translateY(0);
+ }
+}
+
+@keyframes slideDown {
+ from {
+ transform: translateY(0);
+ }
+ to {
+ transform: translateY(8%);
+ }
+}
+
+.dialog {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ max-height: 70vh;
+ outline: none;
+ overflow: hidden;
+}
+
+.menuContent {
+ flex: 1;
+ overflow-y: auto;
+ overflow-x: hidden;
+ padding: 16px;
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ min-height: 0;
+}
diff --git a/docs-ui/src/components/MobileBottomNav/MobileBottomNav.tsx b/docs-ui/src/components/MobileBottomNav/MobileBottomNav.tsx
new file mode 100644
index 0000000000..52ec2ee9d9
--- /dev/null
+++ b/docs-ui/src/components/MobileBottomNav/MobileBottomNav.tsx
@@ -0,0 +1,44 @@
+'use client';
+
+import { useState } from 'react';
+import { RiMoonLine, RiSunLine } from '@remixicon/react';
+import { Button, ToggleButton, ToggleButtonGroup } from 'react-aria-components';
+import styles from './MobileBottomNav.module.css';
+import { usePlayground } from '@/utils/playground-context';
+import { MobileMenu } from './MobileMenu';
+import { AnimatedMenuIcon } from './AnimatedMenuIcon';
+
+export const MobileBottomNav = () => {
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
+ const { selectedTheme, setSelectedTheme } = usePlayground();
+
+ return (
+ <>
+