diff --git a/.changeset/blue-hotels-shake.md b/.changeset/blue-hotels-shake.md new file mode 100644 index 0000000000..3d55aacffa --- /dev/null +++ b/.changeset/blue-hotels-shake.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-scaffolder-common': patch +'@backstage/plugin-scaffolder-react': patch +'@backstage/plugin-scaffolder': patch +--- + +Capturing more event clicks for scaffolder diff --git a/.changeset/brave-carrots-glow.md b/.changeset/brave-carrots-glow.md new file mode 100644 index 0000000000..b4493bf4f6 --- /dev/null +++ b/.changeset/brave-carrots-glow.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-node': minor +--- + +Added `LocationAnalyzer` type, moved from `@backstage/plugin-catalog-backend`. diff --git a/.changeset/bright-pumpkins-rule.md b/.changeset/bright-pumpkins-rule.md new file mode 100644 index 0000000000..07de4f313c --- /dev/null +++ b/.changeset/bright-pumpkins-rule.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +Allow overriding `NotificationsPage` page properties diff --git a/.changeset/chilly-adults-sing.md b/.changeset/chilly-adults-sing.md new file mode 100644 index 0000000000..82f4cb88b2 --- /dev/null +++ b/.changeset/chilly-adults-sing.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Add lifecycle monitoring for the catalog processing diff --git a/.changeset/chilly-shoes-doubt.md b/.changeset/chilly-shoes-doubt.md new file mode 100644 index 0000000000..978600f5b4 --- /dev/null +++ b/.changeset/chilly-shoes-doubt.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-msgraph': patch +--- + +Fixed disabling of user photo fetching. Previously, the config value wasn't propagated properly, so user photos was still being fetched despite disabled by config. diff --git a/.changeset/cold-rats-leave.md b/.changeset/cold-rats-leave.md new file mode 100644 index 0000000000..6fe28d6371 --- /dev/null +++ b/.changeset/cold-rats-leave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Fix issue with the log format not being respected when logging from actions diff --git a/.changeset/cool-elephants-march.md b/.changeset/cool-elephants-march.md new file mode 100644 index 0000000000..2396f9553d --- /dev/null +++ b/.changeset/cool-elephants-march.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gitea': patch +--- + +Allow defining `repoVisibility` field for the action `publish:gitea` diff --git a/.changeset/create-app-1714476054.md b/.changeset/create-app-1714476054.md new file mode 100644 index 0000000000..b50d431d4b --- /dev/null +++ b/.changeset/create-app-1714476054.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Bumped create-app version. diff --git a/.changeset/curly-shirts-flow.md b/.changeset/curly-shirts-flow.md new file mode 100644 index 0000000000..b9a73809ba --- /dev/null +++ b/.changeset/curly-shirts-flow.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': patch +--- + +Update the paths logic in the api reports command to support complex subpaths diff --git a/.changeset/cyan-eagles-hammer.md b/.changeset/cyan-eagles-hammer.md new file mode 100644 index 0000000000..dfe6f0bc62 --- /dev/null +++ b/.changeset/cyan-eagles-hammer.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-notifications-backend-module-email': patch +'@backstage/plugin-notifications-backend': patch +--- + +Fixed email processor `esm` issue and config reading diff --git a/.changeset/cyan-suns-shave.md b/.changeset/cyan-suns-shave.md new file mode 100644 index 0000000000..09e51d9548 --- /dev/null +++ b/.changeset/cyan-suns-shave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-unprocessed': patch +--- + +Correctly convert owner to string in case owner has not been provided diff --git a/.changeset/early-starfishes-hammer.md b/.changeset/early-starfishes-hammer.md new file mode 100644 index 0000000000..abe17e85a2 --- /dev/null +++ b/.changeset/early-starfishes-hammer.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': minor +--- + +Deprecated the `LocationAnalyzer` type, which has been moved to `@backstage/plugin-catalog-node`. diff --git a/.changeset/empty-beers-relax.md b/.changeset/empty-beers-relax.md new file mode 100644 index 0000000000..bbfcef0933 --- /dev/null +++ b/.changeset/empty-beers-relax.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Added `HarnessURLReader` with `readUrl` support. diff --git a/.changeset/fix-stackoverflow.md b/.changeset/fix-stackoverflow.md new file mode 100644 index 0000000000..6ce9f15737 --- /dev/null +++ b/.changeset/fix-stackoverflow.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch +--- + +Fix plugin/incremental-ingestion 'Maximum call stack size exceeded' error when ingest large entities. diff --git a/.changeset/flat-countries-clap.md b/.changeset/flat-countries-clap.md new file mode 100644 index 0000000000..9c962b04a3 --- /dev/null +++ b/.changeset/flat-countries-clap.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': minor +--- + +Adds 2 new commands `repo schema openapi diff` and `package schema openapi diff`. `repo schema openapi diff` is intended to power a new breaking changes check on pull requests and the package level command allows plugin developers to quickly see new API breaking changes. They're intended to be used in complement with the existing `repo schema openapi verify` command to validate your OpenAPI spec against a variety of things. diff --git a/.changeset/four-cooks-serve.md b/.changeset/four-cooks-serve.md new file mode 100644 index 0000000000..864807cc49 --- /dev/null +++ b/.changeset/four-cooks-serve.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-notifications-backend': patch +'@backstage/plugin-notifications-node': patch +--- + +Support for filtering entities from notification recipients after resolving them from the recipients diff --git a/.changeset/funny-bees-taste.md b/.changeset/funny-bees-taste.md new file mode 100644 index 0000000000..a9b2d84997 --- /dev/null +++ b/.changeset/funny-bees-taste.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +The user can newly mark all unread messages as read at one click. diff --git a/.changeset/fuzzy-seahorses-tell.md b/.changeset/fuzzy-seahorses-tell.md new file mode 100644 index 0000000000..f184e9ce95 --- /dev/null +++ b/.changeset/fuzzy-seahorses-tell.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Fixed an internal circular import that broke Jest mocks. diff --git a/.changeset/giant-donkeys-talk.md b/.changeset/giant-donkeys-talk.md new file mode 100644 index 0000000000..62af2eb7e8 --- /dev/null +++ b/.changeset/giant-donkeys-talk.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications-backend-module-email': patch +--- + +Support relative links in notifications sent via email diff --git a/.changeset/green-boxes-rescue.md b/.changeset/green-boxes-rescue.md new file mode 100644 index 0000000000..146695a320 --- /dev/null +++ b/.changeset/green-boxes-rescue.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications-backend-module-email': patch +--- + +Allow sending notifications by email with the new notifications module diff --git a/.changeset/grumpy-toes-tap.md b/.changeset/grumpy-toes-tap.md new file mode 100644 index 0000000000..ff3fbbf2df --- /dev/null +++ b/.changeset/grumpy-toes-tap.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-devtools-backend': patch +--- + +Added discovery property to the readme documentation to ensure that it will build when setting it up as new to a Backstage instance diff --git a/.changeset/happy-radios-kiss.md b/.changeset/happy-radios-kiss.md new file mode 100644 index 0000000000..1097c832d8 --- /dev/null +++ b/.changeset/happy-radios-kiss.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': minor +--- + +The `/alpha` plugin export has had its implementation of the `catalogAnalysisExtensionPoint` updated to reflect the new API. diff --git a/.changeset/heavy-trainers-fly.md b/.changeset/heavy-trainers-fly.md new file mode 100644 index 0000000000..48560b7ddd --- /dev/null +++ b/.changeset/heavy-trainers-fly.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Added config prop `ensureSchemaExists` to support postgres instances where user can create schemas but not databases. diff --git a/.changeset/hot-forks-train.md b/.changeset/hot-forks-train.md new file mode 100644 index 0000000000..a5300085b6 --- /dev/null +++ b/.changeset/hot-forks-train.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +Allow showing notifications as snackbars in the UI diff --git a/.changeset/itchy-keys-wonder.md b/.changeset/itchy-keys-wonder.md new file mode 100644 index 0000000000..879dc096a4 --- /dev/null +++ b/.changeset/itchy-keys-wonder.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +'@backstage/backend-app-api': patch +--- + +Redact `meta` fields too with the logger diff --git a/.changeset/kind-toes-scream.md b/.changeset/kind-toes-scream.md new file mode 100644 index 0000000000..d634f17ce4 --- /dev/null +++ b/.changeset/kind-toes-scream.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +The `versions:bump` command will no longer exit with a non-zero status if the version bump fails due to forbidden duplicate package installations. It will now also provide more information about how to troubleshoot such an error. The set of forbidden duplicates has also been expanded to include all `@backstage/*-app-api` packages. diff --git a/.changeset/late-planes-fix.md b/.changeset/late-planes-fix.md new file mode 100644 index 0000000000..a57d7ec1d4 --- /dev/null +++ b/.changeset/late-planes-fix.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-search-backend-node': patch +'@backstage/plugin-search-backend': patch +--- + +Add lifecycle monitoring for the search index registry diff --git a/.changeset/little-rockets-live.md b/.changeset/little-rockets-live.md new file mode 100644 index 0000000000..767fda917e --- /dev/null +++ b/.changeset/little-rockets-live.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-auth-backend-module-aws-alb-provider': patch +'@backstage/plugin-auth-backend-module-github-provider': patch +'@backstage/plugin-auth-backend': patch +--- + +fix: Move config declarations to appropriate auth backend modules diff --git a/.changeset/loud-timers-flow.md b/.changeset/loud-timers-flow.md new file mode 100644 index 0000000000..d7d0d96970 --- /dev/null +++ b/.changeset/loud-timers-flow.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +Remove explicit `alg` check for user tokens in `verifyToken` diff --git a/.changeset/lovely-games-cry.md b/.changeset/lovely-games-cry.md new file mode 100644 index 0000000000..5209b93496 --- /dev/null +++ b/.changeset/lovely-games-cry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-react': patch +--- + +When using `CookieAuthRefreshProvider` or `useCookieAuthRefresh`, a 404 response from the cookie endpoint will now be treated as if cookie auth is disabled and is not needed. diff --git a/.changeset/mean-ravens-dance.md b/.changeset/mean-ravens-dance.md new file mode 100644 index 0000000000..061e12cd9e --- /dev/null +++ b/.changeset/mean-ravens-dance.md @@ -0,0 +1,7 @@ +--- +'@backstage/backend-common': minor +--- + +Internal refactor of the database code. + +**BREAKING**: The helper functions `createDatabaseClient` and `ensureDatabaseExists` have been removed from the public interface, since they have no usage within the repository and never were suitable for calling from the outside. Please consider using `coreServices.database` or `DatabaseManager` directly wherever possible instead. diff --git a/.changeset/perfect-beers-explode.md b/.changeset/perfect-beers-explode.md new file mode 100644 index 0000000000..3045ca7f12 --- /dev/null +++ b/.changeset/perfect-beers-explode.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +Increase default and allow modifying notification snackbar auto hide duration diff --git a/.changeset/pre.json b/.changeset/pre.json index 87adddcad3..f776d90b91 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -281,7 +281,76 @@ "@backstage/plugin-vault": "0.1.29", "@backstage/plugin-vault-backend": "0.4.10", "@backstage/plugin-vault-node": "0.1.10", - "@backstage/plugin-xcmetrics": "0.2.52" + "@backstage/plugin-xcmetrics": "0.2.52", + "@backstage/plugin-catalog-backend-module-gitlab-org": "0.0.0", + "@backstage/plugin-notifications-backend-module-email": "0.0.0" }, - "changesets": [] + "changesets": [ + "afraid-needles-divide", + "blue-hotels-shake", + "bright-pumpkins-rule", + "chatty-cycles-unite", + "chilly-adults-sing", + "chilly-fireants-roll", + "chilly-shoes-doubt", + "cold-rats-leave", + "cool-elephants-march", + "create-app-1714476054", + "cuddly-chairs-kick", + "curly-shirts-flow", + "curvy-planes-flash", + "cyan-suns-shave", + "dry-sloths-impress", + "eighty-apricots-kneel", + "eighty-bats-stare", + "eleven-pandas-divide", + "fix-stackoverflow", + "fluffy-hotels-wait", + "four-cooks-serve", + "fresh-crews-impress", + "funny-bees-taste", + "fuzzy-seahorses-tell", + "gold-waves-bake", + "gorgeous-cameras-cross", + "green-adults-push", + "green-boxes-rescue", + "hip-carrots-drive", + "hot-forks-train", + "itchy-gorillas-hope", + "itchy-keys-wonder", + "late-planes-fix", + "loud-frogs-eat", + "loud-timers-flow", + "loud-vans-greet", + "lovely-games-cry", + "lucky-news-guess", + "mean-ravens-dance", + "orange-numbers-think", + "perfect-points-hope", + "proud-doors-cheat", + "purple-parents-sin", + "purple-waves-smile", + "rare-fireants-tickle", + "real-crabs-obey", + "renovate-0d0bd5c", + "selfish-pigs-glow", + "shy-students-clap", + "silent-wombats-hang", + "six-scissors-smile", + "smart-avocados-invent", + "smooth-garlics-behave", + "sour-socks-approve", + "stupid-onions-know", + "sweet-zoos-clap", + "tall-ads-shave", + "tasty-apes-learn", + "thick-llamas-itch", + "thick-terms-rush", + "tricky-cougars-shout", + "unlucky-days-play", + "unlucky-rivers-collect", + "warm-fans-promise", + "young-guests-reflect", + "young-olives-drop" + ] } diff --git a/.changeset/rare-fireants-tickle.md b/.changeset/rare-fireants-tickle.md new file mode 100644 index 0000000000..8668d4c287 --- /dev/null +++ b/.changeset/rare-fireants-tickle.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Added optional `initialShowDropDown` prop to `SidebarSubmenuItem` to internally manage the initial display state of the dropdown items. diff --git a/.changeset/real-crabs-obey.md b/.changeset/real-crabs-obey.md new file mode 100644 index 0000000000..9a52f0e5a6 --- /dev/null +++ b/.changeset/real-crabs-obey.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-module-explore': patch +--- + +Migrate search collator to use the new auth services. diff --git a/.changeset/rich-adults-float.md b/.changeset/rich-adults-float.md new file mode 100644 index 0000000000..5a9746b8f5 --- /dev/null +++ b/.changeset/rich-adults-float.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-github': patch +--- + +Updated to use the new `catalogAnalysisExtensionPoint` API. diff --git a/.changeset/selfish-pigs-glow.md b/.changeset/selfish-pigs-glow.md new file mode 100644 index 0000000000..c7808c0283 --- /dev/null +++ b/.changeset/selfish-pigs-glow.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-auth-backend': patch +'@backstage/plugin-auth-node': patch +--- + +Allow overriding default ownership resolving with the new `AuthOwnershipResolutionExtensionPoint` diff --git a/.changeset/selfish-walls-visit.md b/.changeset/selfish-walls-visit.md new file mode 100644 index 0000000000..64c87a64f5 --- /dev/null +++ b/.changeset/selfish-walls-visit.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': patch +--- + +Add `alignGauge` prop to the `GaugeCard`, and a small size version. When `alignGauge` is `'bottom'` the gauge will vertically align the gauge in the cards, even when the card titles span across multiple lines. +Add `alignContent` prop to the `InfoCard`, defaulting to `'normal'` with the option of `'bottom'` which vertically aligns the content to the bottom of the card. diff --git a/.changeset/silent-wombats-hang.md b/.changeset/silent-wombats-hang.md new file mode 100644 index 0000000000..85bda32007 --- /dev/null +++ b/.changeset/silent-wombats-hang.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-msgraph': patch +--- + +Handle fetching huge amounts of users from Azure without crashing diff --git a/.changeset/six-scissors-smile.md b/.changeset/six-scissors-smile.md new file mode 100644 index 0000000000..7680edbba6 --- /dev/null +++ b/.changeset/six-scissors-smile.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +The `SignInPage` guest provider will now fall back to legacy guest auth if the backend request fails, allowing guest auth without a running backend. diff --git a/.changeset/slimy-kids-behave.md b/.changeset/slimy-kids-behave.md new file mode 100644 index 0000000000..5e635878ff --- /dev/null +++ b/.changeset/slimy-kids-behave.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Fixed an issue causing the `repo fix` command to set an incorrect `workspace` property using Windows diff --git a/.changeset/stupid-onions-know.md b/.changeset/stupid-onions-know.md new file mode 100644 index 0000000000..3d028557be --- /dev/null +++ b/.changeset/stupid-onions-know.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +Empty descriptions are not rendered to improve the look&feel. diff --git a/.changeset/sweet-zoos-clap.md b/.changeset/sweet-zoos-clap.md new file mode 100644 index 0000000000..8c76f4abd7 --- /dev/null +++ b/.changeset/sweet-zoos-clap.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Fixed bug in stitching queue gauge that included entities that are scheduled in the future. diff --git a/.changeset/swift-humans-hunt.md b/.changeset/swift-humans-hunt.md new file mode 100644 index 0000000000..b96b3e41bc --- /dev/null +++ b/.changeset/swift-humans-hunt.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Avoiding pre-loading display total count undefined for table counts diff --git a/.changeset/tall-ads-shave.md b/.changeset/tall-ads-shave.md new file mode 100644 index 0000000000..17d58452eb --- /dev/null +++ b/.changeset/tall-ads-shave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications-backend': patch +--- + +Add possibility to generate random notifications on the fly in local development diff --git a/.changeset/tame-jars-double.md b/.changeset/tame-jars-double.md new file mode 100644 index 0000000000..8af92a2d72 --- /dev/null +++ b/.changeset/tame-jars-double.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gitlab': minor +--- + +Add examples for `gitlab:projectVariable:create` scaffolder action & improve related tests diff --git a/.changeset/tasty-apes-learn.md b/.changeset/tasty-apes-learn.md new file mode 100644 index 0000000000..7a4545cf10 --- /dev/null +++ b/.changeset/tasty-apes-learn.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +Fix to show web notifications even when browser is on foreground. Fix duplicate notifications with multiple tabs. diff --git a/.changeset/tasty-moles-jog.md b/.changeset/tasty-moles-jog.md new file mode 100644 index 0000000000..f28b77337e --- /dev/null +++ b/.changeset/tasty-moles-jog.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Add previously-missing semicolon in file templated by `backstage-cli new --select plugin`. diff --git a/.changeset/tasty-rats-explain.md b/.changeset/tasty-rats-explain.md new file mode 100644 index 0000000000..b97ee28fef --- /dev/null +++ b/.changeset/tasty-rats-explain.md @@ -0,0 +1,5 @@ +--- +'@backstage/integration': minor +--- + +Added `HarnessIntegration` via the `ScmIntegrations` interface. diff --git a/.changeset/thick-llamas-itch.md b/.changeset/thick-llamas-itch.md new file mode 100644 index 0000000000..17d4fb23b6 --- /dev/null +++ b/.changeset/thick-llamas-itch.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +Allow defining `className` and additional properties for `NotificationsSideBarItem` diff --git a/.changeset/thick-terms-rush.md b/.changeset/thick-terms-rush.md new file mode 100644 index 0000000000..fb12f11f7e --- /dev/null +++ b/.changeset/thick-terms-rush.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-react': patch +--- + +add the namespace label to CronJobDrawer & IngressDrawer. diff --git a/.changeset/thirty-mangos-travel.md b/.changeset/thirty-mangos-travel.md new file mode 100644 index 0000000000..2281cc06b6 --- /dev/null +++ b/.changeset/thirty-mangos-travel.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search': patch +--- + +Added `aria-label` attribute to DialogTitle element and set `aria-modal` attribute to `true` for improved accessibility in the search modal. diff --git a/.changeset/tough-eggs-wink.md b/.changeset/tough-eggs-wink.md new file mode 100644 index 0000000000..52644e3620 --- /dev/null +++ b/.changeset/tough-eggs-wink.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-node': minor +--- + +Breaking change to `/alpha` API where the `catalogAnalysisExtensionPoint` has been reworked. The `addLocationAnalyzer` method has been renamed to `addScmLocationAnalyzer`, and a new `setLocationAnalyzer` method has been added which allows the full `LocationAnalyzer` implementation to be overridden. diff --git a/.changeset/unlucky-days-play.md b/.changeset/unlucky-days-play.md new file mode 100644 index 0000000000..b847ebd2fc --- /dev/null +++ b/.changeset/unlucky-days-play.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +Fix infinite loop in the notification title counter diff --git a/.changeset/unlucky-rivers-collect.md b/.changeset/unlucky-rivers-collect.md new file mode 100644 index 0000000000..c5ec865658 --- /dev/null +++ b/.changeset/unlucky-rivers-collect.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications-backend': patch +--- + +Updated documentation for sending messages by external services. diff --git a/.changeset/warm-fans-promise.md b/.changeset/warm-fans-promise.md new file mode 100644 index 0000000000..a8a8f8a2c7 --- /dev/null +++ b/.changeset/warm-fans-promise.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +The rendered size of a notification is limited for very long descriptions. diff --git a/.changeset/young-guests-reflect.md b/.changeset/young-guests-reflect.md new file mode 100644 index 0000000000..355c38bc21 --- /dev/null +++ b/.changeset/young-guests-reflect.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +Add option to set the notification as read automatically when the notification link is opened diff --git a/.github/ISSUE_TEMPLATE/plugin.yaml b/.github/ISSUE_TEMPLATE/plugin.yaml deleted file mode 100644 index a7f5216631..0000000000 --- a/.github/ISSUE_TEMPLATE/plugin.yaml +++ /dev/null @@ -1,52 +0,0 @@ -name: 🔌 Plugin -description: 'Submit a proposal for a new Plugin' -title: '🔌 Plugin: ' -labels: [plugin] -body: - - type: markdown - attributes: - value: | - We value your time and efforts to submit this Plugin request form. 🙏 - - type: textarea - id: plugin-summary - validations: - required: true - attributes: - label: '🔖 Summary' - description: 'Provide a general summary of the plugin and how it should work' - placeholder: 'You should add ...' - - type: textarea - id: website - attributes: - label: '🌐 Project website (if applicable)' - description: 'Add a link to the open source project or product this plugin will integrate with, if existing' - placeholder: 'Website Link is ...' - - type: textarea - id: context - attributes: - label: 'âœŒī¸ Context' - description: 'A clear and concise description about the Plugin.' - placeholder: 'Providing additional context' - - type: checkboxes - id: no-duplicate-issues - attributes: - label: '👀 Have you spent some time to check if this plugin request has been raised before?' - options: - - label: "I checked and didn't find similar issue" - required: true - - type: checkboxes - id: read-code-of-conduct - attributes: - label: 'đŸĸ Have you read the Code of Conduct?' - options: - - label: 'I have read the [Code of Conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md)' - required: true - - type: dropdown - id: willing-to-submit-pr - attributes: - label: Are you willing to submit PR? - description: This is absolutely not required, but we are happy to guide you in the contribution process. - options: - - Yes I am willing to submit a PR! - - No, but I'm happy to collaborate on a PR with someone else - - No, I don't have time to work on this right now diff --git a/.github/renovate.json5 b/.github/renovate.json5 index a9cb54912a..1c8d611c49 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -62,6 +62,10 @@ matchPackageNames: ['p-limit'], allowedVersions: '<4.0.0', }, + { + matchPackageNames: ['p-throttle'], + allowedVersions: '<4.0.0', + }, { matchPackageNames: ['p-queue'], allowedVersions: '<7.0.0', diff --git a/.github/vale/config/vocabularies/Backstage/accept.txt b/.github/vale/config/vocabularies/Backstage/accept.txt index b525d7e48e..aa11568564 100644 --- a/.github/vale/config/vocabularies/Backstage/accept.txt +++ b/.github/vale/config/vocabularies/Backstage/accept.txt @@ -152,6 +152,8 @@ graphviz Hackathons haproxy hardcoded +Harness +harness Helidon Henneke Heroku @@ -362,6 +364,8 @@ SIG SIGs siloed Sinon +snackbars +snackbar Snyk Sonarqube sourcemaps @@ -462,6 +466,7 @@ Weaveworks Webpack widget's winston +Wolfi www WWW XCMetrics diff --git a/.github/workflows/api-breaking-changes-comment.yml b/.github/workflows/api-breaking-changes-comment.yml new file mode 100644 index 0000000000..bc90a276f0 --- /dev/null +++ b/.github/workflows/api-breaking-changes-comment.yml @@ -0,0 +1,111 @@ +name: API Breaking Changes (comment) + +on: + workflow_run: + workflows: + - 'API Breaking Changes (Trigger)' + types: + - completed + +jobs: + setup: + name: Add values from previous step + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + permissions: + # "If you specify the access for any of these scopes, all of those that are not specified are set to none." + # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions + actions: read # Access cache + outputs: + git-ref: ${{ steps.event.outputs.GIT_REF }} + pr-number: ${{ steps.event.outputs.PR_NUMBER }} + action: ${{ steps.event.outputs.ACTION }} + steps: + - name: Harden Runner + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + with: + disable-sudo: true + egress-policy: block + allowed-endpoints: > + api.github.com:443 + + - name: 'Download artifacts' + # Fetch output (zip archive) from the workflow run that triggered this workflow. + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.payload.workflow_run.id, + }); + let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { + return artifact.name == "preview-spec" + })[0]; + if (matchArtifact === undefined) { + throw TypeError('Build Artifact not found!'); + } + let download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + let fs = require('fs'); + fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/preview-spec.zip`, Buffer.from(download.data)); + + - name: 'Accept event from first stage' + run: unzip preview-spec.zip event.json + + - name: Read Event into ENV + id: event + run: | + echo PR_NUMBER=$(jq '.number | tonumber' < event.json) >> $GITHUB_OUTPUT + echo ACTION=$(jq --raw-output '.action | tostring | [scan("\\w+")][0]' < event.json) >> $GITHUB_OUTPUT + echo GIT_REF=$(jq --raw-output '.pull_request.head.sha | tostring | [scan("\\w+")][0]' < event.json) >> $GITHUB_OUTPUT + + - name: DEBUG - Print Job Outputs + if: ${{ runner.debug }} + run: | + echo "PR number: ${{ steps.event.outputs.PR_NUMBER }}" + echo "Git Ref: ${{ steps.event.outputs.GIT_REF }}" + echo "Action: ${{ steps.event.outputs.ACTION }}" + cat event.json + + - name: Get Comment + id: get-comment + run: | + unzip preview-spec.zip comment.md + ls + grep + + add-comment: + name: Write comment about issues + needs: + - setup + if: ${{ github.event.workflow_run.conclusion == 'success' }} + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + + # Identify comment to be updated + - name: Find comment for API Changes + uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3 + id: find-comment + with: + issue-number: ${{ needs.setup.outputs.pr-number }} + comment-author: 'github-actions[bot]' + body-includes: API changes + direction: last + + - name: Create or Update Comment with API Changes + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 + with: + comment-id: ${{ steps.find-comment.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + body-path: comment.md + edit-mode: replace diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml new file mode 100644 index 0000000000..901516305d --- /dev/null +++ b/.github/workflows/api-breaking-changes.yml @@ -0,0 +1,56 @@ +name: API Breaking Changes (Trigger) +on: + pull_request: + types: [opened, synchronize, reopened, closed] + paths: + - '**/openapi.yaml' + +jobs: + get-backstage-changes: + env: + NODE_OPTIONS: --max-old-space-size=4096 + name: Build PR image + runs-on: ubuntu-latest + if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }} + steps: + - name: Harden Runner + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + with: + egress-policy: audit + + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + 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 + ref: 'refs/pull/${{ github.event.pull_request.number }}/merge' + - name: fetch base + run: git fetch --depth 1 origin ${{ github.base_ref }} + + - name: setup-node + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + with: + node-version: 18.x + registry-url: https://registry.npmjs.org/ + + - name: yarn install + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 + with: + cache-prefix: linux-v18 + + - name: breaking changes check + run: | + yarn backstage-repo-tools repo schema openapi check --since origin/${{ github.base_ref }} > comment.md + + - name: Upload Rendered Comment as Artifact + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 + with: + name: preview-spec + path: comment.md + retention-days: 2 + + - name: Upload PR Event as Artifact + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 + with: + name: preview-spec + path: ${{ github.event_path }} + retention-days: 2 diff --git a/.github/workflows/automate_changeset_feedback.yml b/.github/workflows/automate_changeset_feedback.yml index 4d965499d2..fefc73bd9f 100644 --- a/.github/workflows/automate_changeset_feedback.yml +++ b/.github/workflows/automate_changeset_feedback.yml @@ -27,7 +27,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 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 38243ee0ac..d6520d16a6 100644 --- a/.github/workflows/automate_merge_message.yml +++ b/.github/workflows/automate_merge_message.yml @@ -28,7 +28,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: ref: '${{ github.event.pull_request.merge_commit_sha }}' diff --git a/.github/workflows/automate_stale.yml b/.github/workflows/automate_stale.yml index 691066fb61..c7674e853d 100644 --- a/.github/workflows/automate_stale.yml +++ b/.github/workflows/automate_stale.yml @@ -28,7 +28,7 @@ jobs: for your contributions. days-before-issue-stale: 60 days-before-issue-close: 7 - exempt-issue-labels: plugin,after-vacations,will-fix + exempt-issue-labels: plugin,after-vacations,will-fix,bep stale-issue-label: stale stale-pr-message: > This PR has been automatically marked as stale because it has not had diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fdd31b2d57..c819ae8130 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 @@ -68,7 +68,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 @@ -197,7 +197,7 @@ jobs: INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }} steps: - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: fetch master branch run: git fetch origin master diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index 020254c872..8c263fdd4f 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -25,7 +25,7 @@ jobs: egress-policy: audit - name: checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: path: backstage ref: ${{ github.event.client_payload.version && env.RELEASE_VERSION || github.ref }} diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index 6864ec67dc..b955b39b59 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -28,7 +28,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js 18.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/deploy_nightly.yml b/.github/workflows/deploy_nightly.yml index 07c3db0f11..a3a1b5e283 100644 --- a/.github/workflows/deploy_nightly.yml +++ b/.github/workflows/deploy_nightly.yml @@ -19,7 +19,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js 18.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index 775d8d00f8..0c44d32384 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -65,7 +65,7 @@ jobs: INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }} steps: - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 @@ -105,11 +105,11 @@ jobs: - name: test (and upload coverage) run: | yarn backstage-cli repo test --maxWorkers=3 --workerIdleMemoryLimit=1300M --coverage - bash <(curl -s https://codecov.io/bash) + # bash <(curl -s https://codecov.io/bash) # Upload code coverage for some specific flags. Also see .codecov.yml - bash <(curl -s https://codecov.io/bash) -f packages/core-app-api/coverage/* -F core-app-api - bash <(curl -s https://codecov.io/bash) -f packages/core-components/coverage/* -F core-components - bash <(curl -s https://codecov.io/bash) -f packages/core-plugin-api/coverage/* -F core-plugin-api + # bash <(curl -s https://codecov.io/bash) -f packages/core-app-api/coverage/* -F core-app-api + # bash <(curl -s https://codecov.io/bash) -f packages/core-components/coverage/* -F core-components + # bash <(curl -s https://codecov.io/bash) -f packages/core-plugin-api/coverage/* -F core-plugin-api env: BACKSTAGE_TEST_DISABLE_DOCKER: 1 BACKSTAGE_TEST_DATABASE_POSTGRES16_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres16.ports[5432] }} @@ -148,7 +148,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 91f1b02fee..5acd11c9b0 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -34,7 +34,7 @@ jobs: egress-policy: audit - name: 'Checkout code' - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false @@ -66,6 +66,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 + uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 with: sarif_file: results.sarif diff --git a/.github/workflows/sync_code-formatting.yml b/.github/workflows/sync_code-formatting.yml index 5dbd0361c1..a59eea9c9b 100644 --- a/.github/workflows/sync_code-formatting.yml +++ b/.github/workflows/sync_code-formatting.yml @@ -14,7 +14,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 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 8b3be335ed..88997ea5dd 100644 --- a/.github/workflows/sync_dependabot-changesets.yml +++ b/.github/workflows/sync_dependabot-changesets.yml @@ -16,7 +16,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 2 ref: ${{ github.head_ref }} diff --git a/.github/workflows/sync_release-manifest.yml b/.github/workflows/sync_release-manifest.yml index b0a2499d43..1549ec0e90 100644 --- a/.github/workflows/sync_release-manifest.yml +++ b/.github/workflows/sync_release-manifest.yml @@ -21,7 +21,7 @@ jobs: run: npm install semver@7.3.5 fs-extra@10.0.0 @manypkg/get-packages@1.1.1 - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: path: backstage # 'v' prefix is added here for the tag, we keep it out of the manifest logic @@ -29,7 +29,7 @@ jobs: # Checkout backstage/versions into /backstage/versions, which is where store the output - name: Checkout versions - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: repository: backstage/versions path: backstage/versions diff --git a/.github/workflows/sync_renovate-changesets.yml b/.github/workflows/sync_renovate-changesets.yml index d02e35483c..e95a68ac2f 100644 --- a/.github/workflows/sync_renovate-changesets.yml +++ b/.github/workflows/sync_renovate-changesets.yml @@ -16,7 +16,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 2 ref: ${{ github.head_ref }} diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml index d5a4048044..bebbbc4fbe 100644 --- a/.github/workflows/sync_snyk-github-issues.yml +++ b/.github/workflows/sync_snyk-github-issues.yml @@ -16,7 +16,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js 18.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index 70fb8a44d9..cb3efcfac3 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -29,7 +29,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Monitor and Synchronize Snyk Policies uses: snyk/actions/node@8349f9043a8b7f0f3ee8885bf28f0b388d2446e8 # master with: @@ -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@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 + uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 with: sarif_file: snyk.sarif diff --git a/.github/workflows/sync_version-packages.yml b/.github/workflows/sync_version-packages.yml index b2ad80b6da..82a0eae7be 100644 --- a/.github/workflows/sync_version-packages.yml +++ b/.github/workflows/sync_version-packages.yml @@ -18,7 +18,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 20000 fetch-tags: true diff --git a/.github/workflows/uffizzi-build.yml b/.github/workflows/uffizzi-build.yml index 4a52af21bc..11a0054703 100644 --- a/.github/workflows/uffizzi-build.yml +++ b/.github/workflows/uffizzi-build.yml @@ -31,7 +31,7 @@ jobs: egress-policy: audit - name: checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: setup-node uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 @@ -89,7 +89,7 @@ jobs: egress-policy: audit - name: Checkout git repo - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Render Compose File run: | # update image after the build above diff --git a/.github/workflows/verify_accessibility.yml b/.github/workflows/verify_accessibility.yml index 1c25d709cb..b4615fee0a 100644 --- a/.github/workflows/verify_accessibility.yml +++ b/.github/workflows/verify_accessibility.yml @@ -24,7 +24,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Use Node.js 18.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml index e82577763e..60cb89a045 100644 --- a/.github/workflows/verify_codeql.yml +++ b/.github/workflows/verify_codeql.yml @@ -47,7 +47,7 @@ jobs: egress-policy: audit - name: Checkout repository - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 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@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 + uses: github/codeql-action/init@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 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@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 + uses: github/codeql-action/autobuild@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 # â„šī¸ 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@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 + uses: github/codeql-action/analyze@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index 3550df6fc0..2eb7608ccd 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -16,7 +16,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 # 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-kubernetes.yml b/.github/workflows/verify_e2e-kubernetes.yml index 6ff41c8b74..3c425c25e9 100644 --- a/.github/workflows/verify_e2e-kubernetes.yml +++ b/.github/workflows/verify_e2e-kubernetes.yml @@ -26,7 +26,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml index a750dfea3b..75c7a800d8 100644 --- a/.github/workflows/verify_e2e-linux.yml +++ b/.github/workflows/verify_e2e-linux.yml @@ -45,7 +45,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Configure Git run: | diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml index 3bcda5f2b6..d5f2594285 100644 --- a/.github/workflows/verify_e2e-techdocs.yml +++ b/.github/workflows/verify_e2e-techdocs.yml @@ -34,7 +34,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: '3.9' diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index 078ddd6855..a6fa469666 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -42,7 +42,7 @@ jobs: git config --global core.autocrlf false git config --global core.eol lf - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Configure Git run: | diff --git a/.github/workflows/verify_fossa.yml b/.github/workflows/verify_fossa.yml index 801c5fbd28..ccc9e4509a 100644 --- a/.github/workflows/verify_fossa.yml +++ b/.github/workflows/verify_fossa.yml @@ -19,7 +19,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - 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.yml b/.github/workflows/verify_microsite.yml index 95bca2725d..db6a3e2d40 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -28,12 +28,18 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js 18.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: node-version: 18.x + - uses: actions/setup-python@v5 + with: + python-version: '3.9' + + - name: Install MkDocs dependencies + run: pip install mkdocs mkdocs-techdocs-core # Skip caching of microsite dependencies, it keeps the global cache size # smaller, which make Windows builds a lot faster for the rest of the project. @@ -47,9 +53,6 @@ jobs: - name: build API reference run: yarn build:api-docs - - name: Install MkDocs dependencies - run: pip3 install mkdocs mkdocs-techdocs-core - - name: Build MkDocs for TechDocs run: mkdocs build --strict diff --git a/.github/workflows/verify_microsite_accessibility.yml b/.github/workflows/verify_microsite_accessibility.yml index e0fea1a72c..19417c5cee 100644 --- a/.github/workflows/verify_microsite_accessibility.yml +++ b/.github/workflows/verify_microsite_accessibility.yml @@ -19,7 +19,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Use Node.js 18.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/verify_storybook.yml b/.github/workflows/verify_storybook.yml index 940c136797..6a37f5abb6 100644 --- a/.github/workflows/verify_storybook.yml +++ b/.github/workflows/verify_storybook.yml @@ -32,7 +32,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 0 # Required to retrieve git history diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml index 32b0452eec..c7ff8f5ca9 100644 --- a/.github/workflows/verify_windows.yml +++ b/.github/workflows/verify_windows.yml @@ -33,7 +33,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.yarn/patches/@changesets-assemble-release-plan-npm-6.0.0-f7b3005037.patch b/.yarn/patches/@changesets-assemble-release-plan-npm-6.0.0-f7b3005037.patch new file mode 100644 index 0000000000..045e983dbe --- /dev/null +++ b/.yarn/patches/@changesets-assemble-release-plan-npm-6.0.0-f7b3005037.patch @@ -0,0 +1,32 @@ +diff --git a/dist/changesets-assemble-release-plan.cjs.js b/dist/changesets-assemble-release-plan.cjs.js +index ee5c0f67fabadeb112e9f238d8b144a4d125830f..9b0e1a156dd88cee35f82faf718d82a8a8f80325 100644 +--- a/dist/changesets-assemble-release-plan.cjs.js ++++ b/dist/changesets-assemble-release-plan.cjs.js +@@ -179,12 +179,23 @@ function getDependencyVersionRanges(dependentPkgJSON, dependencyRelease) { + if (!versionRange) continue; + + if (versionRange.startsWith("workspace:")) { ++ // intentionally keep other workspace ranges untouched ++ // this has to be fixed but this should only be done when adding appropriate tests ++ let workspaceRange = versionRange.replace(/^workspace:/, ""); ++ switch (workspaceRange) { ++ case "*": ++ // workspace:* actually means the current exact version, and not a wildcard similar to a reguler * range ++ workspaceRange = dependencyRelease.oldVersion; ++ break; ++ case "~": ++ case "^": ++ // Use ^oldVersion for workspace:^ or ~oldVersion for workspace:~. ++ // The version range might have changed in dependent package, but that should have its own changeset bumping that package. ++ workspaceRange += dependencyRelease.oldVersion; ++ } + dependencyVersionRanges.push({ + depType: type, +- versionRange: // intentionally keep other workspace ranges untouched +- // this has to be fixed but this should only be done when adding appropriate tests +- versionRange === "workspace:*" ? // workspace:* actually means the current exact version, and not a wildcard similar to a reguler * range +- dependencyRelease.oldVersion : versionRange.replace(/^workspace:/, "") ++ versionRange: workspaceRange, + }); + } else { + dependencyVersionRanges.push({ diff --git a/ADOPTERS.md b/ADOPTERS.md index 32c136fc52..3878563683 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -262,6 +262,7 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/ | [Cielo](https://www.cielo.com.br) | [@Alex Silva](https://github.com/narokwq) | We are using as our Internal Developer Portal, it provides developers with the resources, information, and tools they need to build high-quality applications and adhere to organizational standards and security practices. | | [Giant Swarm](https://giantswarm.io) | [Lucas Weatherhog](https://github.com/weatherhog) | At Giant Swarm we are using Backstage as our internal developer portal. Heavily using the service catalog and the TechDocs plugin. Further looking into adopting functionalities of our Web UI into Backstage to have a single pane of glass for our core business, managing all our components, and helping developers to be more productive. | | [WD Studios](https://wdstudios.tech) | [Mikael Aboagye](https://github.com/JailbreakPapa) | We are using Backstage as our main internal developer portal for our Programmers and Studio Partners. It's really proven its worth at our studio with on-boarding new programmers! | +| [Anuclei](https://anuclei.com) | [Mark Jackson](https://github.com/markjacksonfishing) | At Anuclei, we are using Backstage as our internal portal for developers, providing all the tools and guidance needed for full-stack software projects. It simplifies our development methods, making innovation and investment in new ideas consistent and easy to repeat. | [Fortnox](https://www.fortnox.se) | [@magnusp](https://github.com/magnusp) | With Backstage we consolidate our internal tools into a single pane of glass and let the catalog drive our user interface. Our software templates onboard our services and teams onto Backstage, and with Techdocs we are letting our developers get back in control of their own documentation. | | [Wefox](https://www.wefox.com) | [@dibenlloch](https://github.com/dibenlloch) | Internal developer portal to consolidate all developers dashboards, company inventory with compliance reports, team management and self-service portal for IT/Team operations. | | [Aurora Innovation](https://aurorainnovation.com) | [@O5ten](https://github.com/O5ten) | Heavy usage of scaffolder, techdocs, homepage, k8s plugin and homegrown plugins to track migration paths and so on within a developer portal. It acts as a starting point for new and old developers to find all of our internal tooling in one place. diff --git a/GOVERNANCE.md b/GOVERNANCE.md deleted file mode 100644 index 2367dab99c..0000000000 --- a/GOVERNANCE.md +++ /dev/null @@ -1,233 +0,0 @@ -# Project Areas - -The Backstage project is divided into several project areas, each covering particular parts of the project. The main driver for each area is ownership of code in the main Backstage repository, as well as other repositories in the Backstage GitHub organization. Each area has a set of maintainers and repository content that they own. There may be no overlap in ownership between areas, which means that any given line in the GitHub code owners file should have only one owner specified. Each area is represented by a team in the Backstage GitHub organization. Apart from certain project-wide concerns, such as the release process, each area is self-governing and chooses their own ways of working. Project areas may also have special interest groups (SIGs) related to their area, but this is not required. - -The project areas as well as their maintainers are listed in the [OWNERS.md](./OWNERS.md) file. - -Each project area must have at least one maintainer. Project area maintainers may have shared ownership with the core maintainers, which in that case is considered an incubating area. The project area maintainers help drive work forward in the area, but they might not yet feel ready to take on ownership. The goal should generally be that these project area maintainers eventually become sole maintainers of the project area. This is to allow for a more smooth onboarding and transition of ownership, where members of the community might for example be new to open source maintainership. - -## Adding new project areas - -Project areas are added by nominating new maintainers for that area. See the sections for becoming a [Project Area Maintainer](#project-area-maintainer). - -Project areas may also by added by splitting existing areas. Every area that is created through this process must have at least one maintainer. - -## Removing project areas - -Project areas are removed by removing all maintainers for that area and removing the corresponding team from the Backstage GitHub organization. The project area can be re-added later if there is a need for it. Reasons for removal may include lack of activity, lack of maintainers, or lack of relevance to the project. - -# Project Roles - -## Contributor - -A Contributor contributes directly to the project and adds value to it. Contributions need not be code. People at the Contributor level may be new contributors, or they may only contribute occasionally. - -### Responsibilities - -- Follow the [CNCF CoC](https://github.com/cncf/foundation/blob/main/code-of-conduct.md) -- Follow the project [contributing guide](CONTRIBUTING.md) - -### How to get involved - -- Participate in community discussions -- Help other users -- Submit bug reports -- Comment on issues -- Try out new releases -- Attend community events - -### How to contribute - -- Report and sometimes resolve issues -- Occasionally submit PRs -- Contribute to the documentation -- Show up at meetings, take notes -- Answer questions from other community members -- Submit feedback on issues and PRs -- Test releases and patches and submit reviews -- Run or help run events -- Promote the project in public - -## Organization Member - -An org member is a frequent contributor that has become a member of the Backstage GitHub organization. In addition to the responsibilities of contributors, an org member is also expected to be reasonably active in the community through continuous contributions of any type. - -An Organization Member must meet the responsibilities and has the requirements of a Contributor. - -### Responsibilities - -- Continues to contribute regularly, as demonstrated by having at least 10 GitHub contributions per year in [Devstats](https://backstage.devstats.cncf.io/d/48/users-statistics-by-repository-group?orgId=1&var-period=y&var-metric=contributions&var-repogroup_name=All&from=now-1y&to=now&var-users=All), or contributions of a similar effort that might not be captured in Devstats. - -### Requirements - -- Must have at least 10 contributions to the projects in the form of: - - Accepted PRs - - Helpful PR reviews - - Resolving GitHub issues - - Or some equivalent contributions to the project -- Must have been contributing for at least 3 months -- Or is the member of a team that owns a project area, in which case the above requirements do not apply and the member is instead vetted by the project area maintainers - -### Becoming an Organization Member - -Open an issue towards [the community repository](https://github.com/backstage/community) using the [org membership request template](https://github.com/backstage/community/issues/new?template=org_member.yaml&title=Org+Member%3A+%3Cyour-github-login%3E). - -### Privileges - -- Membership in the Backstage GitHub organization - -## Plugin Maintainer - -A Plugin Maintainer is responsible for maintaining an individual Backstage plugin or module. This includes reviewing contributions and responding to issues towards an individual plugin, as well as keeping the plugin up to date. - -Plugin Maintainer is a lightweight form of ownership that is primarily reflected though code owners of the plugin packages in the [CODEOWNERS](./.github/CODEOWNERS) file. Each plugin can have one or more maintainers. If a plugin becomes a significant part of the Backstage ecosystem, it may be promoted to be a distinct project area instead. - -A Plugin Maintainer has all the rights and responsibilities of an Organization Member. - -### Responsibilities - -- Review the majority of PRs towards the plugin -- Respond to GitHub issues related to the plugin -- Keep the plugin up-to-date with Backstage libraries and other dependencies -- Follow the [reviewing guide](REVIEWING.md) - -### Requirements - -- Is an Organization Member -- Display knowledge of Backstage's review process and best practices for plugin design -- Is supportive of new and occasional contributors and helps get useful PRs in shape to merge - -### Privileges - -- GitHub code owner of the plugin directory, with rights to approve and merge PRs towards the plugin - -### Becoming a Plugin Maintainer - -To become a Plugin Maintainer, you first need to be an Organization Member. You can then file a pull request towards [CODEOWNERS.md](./.github/CODEOWNERS) requesting to be added as a code owner of the plugin directory. Existing code owners of that plugin alongside the core maintainers will then review the request. - -## Project Area Maintainer - -Project Area Maintainers are owners of a particular project area. They are expected to review and merge pull requests towards their area, and also drive development and manage tech health. A Project Area Maintainer also need to commit a certain number of hours per month towards the project, and exercise judgment for the good of the project, independent of their employer. Project Area Maintainers should also mentor new maintainers and participate in and lead community meetings related to their area. New Project Area Maintainers need to be approved by the existing project area maintainers, or the core maintainers if it is a new area. - -The maintainers of a project area may be represented by a team in external organization. In this case, the state as a project area maintainer is tied to the membership in that team. New members of the team may automatically be added as maintainers, as well as removed when they leave. This process is governed autonomously by the team of project area maintainers. - -A Project Area Maintainer has all the rights and responsibilities of an Organization Member. - -### Responsibilities - -- Review PRs towards their project area. Project area maintainers are expected to review at least 20 PRs per year, or the majority of all PRs towards the area, if it is less than 20 -- Follow the [reviewing guide](REVIEWING.md) -- Triage and respond to issues related to their project area -- Mentor new project area maintainers -- Write refactoring PRs -- Determine strategy and policy for the project area -- Participate in or leading community meetings related to their project area - -### Requirements - -- Is an Organization Member -- Have made at least 5 meaningful contributions towards the project area -- Demonstrates knowledge of their project area, and how it fits into the larger Backstage project -- Is able to exercise judgment for the good of the project, independent of their employer, friends, or team -- Mentors other contributors and project area maintainers -- Can commit to spending at least 16 hours per month working on the project, preferably distributed evenly across the month - -### Privileges - -- Approve and merge PRs towards their project area -- Drive the direction and roadmap of their project area - -### Becoming a Project Area Maintainer - -If you are interested in becoming a project area maintainer, reach out to the existing maintainers for that area. If you wish to become a maintainer for a new area, reach out to the core maintainers. - -Any current project area maintainer or core maintainer may nominate a new project area maintainer by opening a PR towards the [OWNERS.md](OWNERS.md) file. A majority of the project area maintainers for that area must approve the PR. If there are no existing maintainers for that area, the PR must be approved by a majority of the core maintainers. - -## Core Maintainer - -Core Maintainers are responsible for the Backstage project as a whole. They help review and merge project-level pull requests as well as coordinate work affecting multiple project areas. A core maintainer needs to commit the majority of their working time towards the project, and exercise judgment for the good of the project, independent of their employer. Core maintainers should also mentor and seek out new maintainers, lead community meetings, and communicate with the CNCF on behalf of the project. To become a core maintainer one needs to have been the maintainer of a number of different project areas, demonstrate a deep knowledge of large parts of the Backstage project, and be backed by the existing core maintainers. - -A Core Maintainer have all the rights and responsibilities of a Project Area Maintainer. - -### Responsibilities - -- Take part in the incoming issue and PR triage and review process. PRs are shared equally among all maintainers -- Mentor new Project Area Maintainers and Plugin Maintainers -- Drive refactoring and manage tech health across the entire project -- Participate in CNCF maintainer activities -- Respond to security incidents in accordance to our [security policy](./SECURITY.md) -- Determine strategy and policy for the project -- Participate in or leading community meetings - -### Requirements - -- Experience as a Project Area Maintainer for at least 6 months -- Demonstrates a broad knowledge of the project across multiple areas -- Is able to exercise judgment for the good of the project, independent of their employer, friends, or team -- Mentors other contributors -- Can commit to spending at least 10 days per month working on the project - -### Privileges - -- Approve PRs that fall outside any specific project area -- Merge PRs to any area of the project -- Represent the project in public as a Maintainer -- Communicate with the CNCF on behalf of the project -- Have a vote in Maintainer decision-making meetings - -### Becoming a Core Maintainer - -Any core maintainer or end user sponsor may nominate a new core maintainer by opening a PR towards the [OWNERS.md](OWNERS.md) file. Core maintainers must be approved by a majority of the existing core maintainers and end user sponsors. - -## End User Sponsors - -### Role of a Backstage End User Sponsor - -- Provide support for Backstage by removing blockers, securing funding, providing advocacy, feedback, and ensuring project continuity and long term success -- Assist Backstage maintainers in prioritizing upcoming roadmap items and planned work -- Provide neutral mediation for any disputes that arise as part of the project - -### Backstage End User Sponsor Membership - -The End User Sponsors group comprises at most 5 people. To be eligible for membership in the group, you or the company where you work you must: - -- Be responsible for and end user of a production Backstage deployment of non-trivial size -- Be active contributors to the open source project -- Be willing and able to attend regularly-scheduled End User Sponsor meetings -- Abide by [CNCF CoC](https://github.com/cncf/foundation/blob/main/code-of-conduct.md) - -Candidates for membership will be nominated by current Sponsor members or by Backstage maintainers. If there are more nominations than Sponsor seats remaining, existing sponsors shall vote on the candidates, and the candidates with the most votes will become Sponsors. Any ties will be broken by current Backstage sponsors. - -# Conflict resolution and voting - -In general, we prefer that technical issues and membership are amicably worked out between the persons involved. If a dispute cannot be decided independently, the sponsors and core maintainers can be called in to decide an issue. If the sponsors and maintainers themselves cannot decide an issue, the issue will be resolved by voting. - -In all cases in this document where voting is mentioned, the voting process is a simple majority in which each sponsor receives two votes and each core maintainer receives one vote. If such a majority is reached, the vote is said to have _passed_. - -## Inactivity - -It is important for contributors to be and stay active to set an example and show commitment to the project. Inactivity is harmful to the project as it may lead to unexpected delays, contributor attrition, and a loss of trust in the project. - -Inactivity is measured by periods of no contributions without explanation, for longer than: - -- Core Maintainer: 2 months -- Project Area Maintainer: 4 months -- Plugin Maintainer: 6 months -- Organization Member: 12 months - -Consequences of being inactive include: - -- Involuntary removal or demotion -- Being asked to move to Emeritus status - -## Involuntary Removal or Demotion - -Involuntary removal/demotion of a contributor happens when responsibilities and requirements aren't being met. This may include repeated patterns of inactivity, extended period of inactivity, a period of failing to meet the requirements of your role, and/or a violation of the Code of Conduct. This process is important because it protects the community and its deliverables while also opens up opportunities for new contributors to step in. - -Involuntary removal or demotion is handled through a vote by a majority of the current Core Maintainers. Some aspects of this process may be automated, such as removal after periods of inactivity. - -## Stepping Down/Emeritus Process - -If and when contributors' commitment levels change, contributors can consider stepping down (moving down the contributor ladder) vs moving to emeritus status (completely stepping away from the project). - -Contact the Maintainers about changing to Emeritus status, or reducing your contributor level. diff --git a/NOTICE b/NOTICE index fb23d28ebc..1f1dfbccbb 100644 --- a/NOTICE +++ b/NOTICE @@ -5,3 +5,4 @@ Portions of this software were developed by third-party software vendors: - Tech Radar Plugin (https://opensource.zalando.com/tech-radar/), Copyright (c) 2017 Zalando SE - [OpenAPI Generator Templates](./packages/repo-tools/templates), Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) Copyright 2018 SmartBear Software +- Optic CLI (https://github.com/opticdev/optic), Copyright 2022, Optic Labs Corporation diff --git a/OWNERS.md b/OWNERS.md index ab35b5b1e5..d8967ca873 100644 --- a/OWNERS.md +++ b/OWNERS.md @@ -1,5 +1,5 @@ - See [CONTRIBUTING.md](CONTRIBUTING.md) for general contribution guidelines. -- See [GOVERNANCE.md](GOVERNANCE.md) for governance guidelines and responsibilities. +- See [GOVERNANCE.md](https://github.com/backstage/community/blob/main/GOVERNANCE.md) for governance guidelines and responsibilities. ## Core Maintainers diff --git a/app-config.yaml b/app-config.yaml index 9e5b5c468c..cb70170541 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -9,6 +9,9 @@ app: # applicationId: qwerty # site: # datadoghq.eu default = datadoghq.com # env: # optional + # sessionSampleRate: 100 + # sessionReplaySampleRate: 0 + support: url: https://github.com/backstage/backstage/issues # Used by common ErrorPage items: # Used by common SupportButton component diff --git a/beps/0007-auth-external-services/README.md b/beps/0007-auth-external-services/README.md index c8c20879d0..d49cd7992f 100644 --- a/beps/0007-auth-external-services/README.md +++ b/beps/0007-auth-external-services/README.md @@ -12,9 +12,7 @@ creation-date: 2024-03-30 # BEP: Authentication of External Services -<!-- Before merging the initial BEP PR, create a feature issue and update the below link. You can wait with this step until the BEP is ready to be merged. --> - -[**Discussion Issue**](https://github.com/backstage/backstage/issues/NNNNN) +[**Discussion Issue**](https://github.com/backstage/backstage/issues/24349) - [Summary](#summary) - [Motivation](#motivation) @@ -28,17 +26,15 @@ creation-date: 2024-03-30 ## Summary -This proposal builds upon the foundation laid out in the 0003-auth-architecture-evolution BEP by introducing an authentication mechanism designed for external callers seeking to access the REST APIs exposed by Backstage or its plugins. +This proposal builds upon the foundation laid out in the [Auth Architecture Evolution BEP](https://github.com/backstage/backstage/tree/master/beps/0003-auth-architecture-evolution), by improving the authentication mechanisms used by external callers seeking to access the REST APIs exposed by Backstage plugins. -This addition is not intended to supplant or offer an alternative to existing JWT tokens utilized for backend-to-backend or frontend-to-backend communication. JWT tokens remain a viable option for external callers. However, this proposal presents additional options to simplify usage, especially in heterogeneous setups. +Specifically, the inclusion of shared-key based authentication is proposed, along with a framework for integrating different authentication types in the future. Compatibility with the legacy shared-secret token signature mechanism is retained. -Specifically, the inclusion of shared-key based authentication is proposed, along with a framework for integrating different authentication types in the future. - -By implementing this proposed solution, security should not be compromised beyond the existing state when exposing the API to the internet. Nonetheless, it is advisable to maintain a level of protection by shielding the API from direct internet access. +By implementing this proposed solution, security should not be compromised beyond the existing state when exposing the API to the internet. Nonetheless, it is advisable to maintain a level of protection by shielding your deployments from the internet access unless you explicitly need public access. ## Motivation -This proposal represents a progression from the tutorial in https://backstage.io/docs/auth/service-to-service-auth/#usage-in-external-callers in the context of the new Authentication service. +This proposal represents a progression from [the old service to service auth tutorial](https://backstage.io/docs/auth/service-to-service-auth--old/#usage-in-external-callers) in the context of the new `coreServices.auth` and `coreServices.httpAuth` authentication services. It offers expanded configuration possibilities and will include ready-to-use examples as part of the implementation to streamline the process of invoking REST services exposed by Backstage backend plugins for external services. @@ -46,12 +42,16 @@ The previous tutorial lacked clarity on how to effectively structure the call, r Examples of potential use-cases include: -- An external asynchronous service requiring the ability to send notifications to users. To facilitate this process, no code change but configuration only is needed on the Backstage side. +- An external asynchronous service requiring the ability to send notifications to users +- Integrations for external services interacting with the catalog +- Temporary `curl` friendly tokens in local development + +To facilitate this process, no code change but configuration only is needed on the Backstage side. ### Goals - The primary objective is to facilitate access for external heterogeneous services to the Backstage REST API without involving unnecessary complexity. -- This addition is entirely optional. JWT tokens remain mandatory for all internal communication within Backstage. +- Moving to the proposed new auth scheme is entirely optional. The old shared key signature tokens remain usable. - Configuration options will allow specification of which API endpoints can be accessed by an external entity. - Limiting API context and granting distinct external access independently helps mitigate security risks. A compromised external service can only access a restricted portion of the API. - Enhanced clarity and updated examples make it easy to comprehend and utilize. @@ -60,54 +60,108 @@ Examples of potential use-cases include: It is not a goal -- to replace the current Backstage service-to-service authentication or tokens, -- to offer a bypass option for Backstage plugins to circumvent existing service-to-service authentication. Even if feasible, such a bypass would be considered as an anti-pattern. +- To replace the current Backstage service-to-service authentication or tokens, +- To offer a bypass option for Backstage plugins to circumvent existing service-to-service authentication. Even if feasible, such a bypass would be considered as an anti-pattern. ## Proposal -The `app-config.yaml` `backend.auth.keys` property will become an array containing alternative authentication mechanisms for callers. +The `backend.auth.keys` section of your app config remains supported for the time being, but is considered legacy. It is read by the new auth handler code solely for backward compatibility reasons. Using this format will lead to a logged warning urging a migration to the new format described below. -JWT tokens remain obligatory for internal Backstage calls and become optional for external callers. Given the complexity associated with composing JWT tokens by external entities, alternative authentication options will be provided for external callers: +This BEP adds a `backend.auth.externalAccess` section to your app config. This section is the new recommended way of declaring all methods of external service access that you want to support. Like the legacy section above, it is an array, but each element has a `type` field to allow for extensibility. Using an array also means that the configuration system will not make any config merging of access methods across different config files - which avoids unintended mistakes that might otherwise have impacted security. -``` -backend: - auth: - keys: - - type: static - value: ${SERVICE_API_KEY} - context: '/' - - type: jwks - value: https://other-service.acme.org/.well-known/jwks.json - context: '/foo' - - type: certificate - value: - $file: ./service-cert.pem - context: '/foo/bar' -``` - -The static type is designed for a straightforward shared-key strategy. In this approach, an external service provides a secret string to authenticate itself and access REST resources within a specified context. The shared key is transmitted via a new request header, `authorization_static`. - -Ideally, each external service should have its own unique secret key, even when accessing the same context. If a service requires access to different, separate contexts, multiple corresponding entries will be added to the configuration. - -While other authentication types are provided as examples for potential future expansions, they will not be implemented in the initial phase. However, additional types, including those not yet defined, can be introduced in subsequent phases. - -Introducing a new external caller with its exclusive secret key entails updating the app-config.yaml file and restarting the backend. This mechanism is designed to support integration with selected services, and therefore dynamic addition of callers is not necessary. - -New type-specific functions, such as authenticateStatic(), will be integrated into the AuthService, alongside the existing authenticate() function, which will remain dedicated to JWT tokens. - -No changes will be made to the existing APIs, as any necessary adjustments can be incorporated within private functions of the existing DefaultHttpAuthService implementation. - -Specifically, the DefaultHttpAuthService.extractCredentialsFromRequest function will continue to retrieve a bearer token in JWT format from the authorization header as before. If this header is absent, the service will proceed to read and manage the header corresponding to the next authentication type. - -Furthermore, the options parameter of the HttpAuthService.credentials() function will be expanded to include a new context property. This property will be compared to the value of the context configuration property associated with the corresponding authentication key. If the context parameter is not a subset of the configuration property, the request will be denied. +The two types of external access covered by this BEP are `legacy` and `static`. More on that below. ## Design Details -<!-- -This section should contain enough information that the specifics of your -change are understandable. This may include API specs or even code snippets. -If there's any ambiguity about HOW your proposal will be implemented, this is the place to discuss them. ---> +### Configuration + +The following is an example of what an app config section with a single access method could look like: + +```yaml +backend: + auth: + externalAccess: + - type: static + options: + token: ${SERVICE_API_TOKEN} + scope: + plugins: catalog +``` + +Each entry has a `type`, which is any string. The framework handles a number of these types - initially a fixed set, but in the future we might make it extensible. + +The `options` is a general object, and is specific to each type, configuring it as needed. This particular type only had a `token`, which was configured to be taken from an environment variable. + +Each entry may also optionally have a `scope` section that controls what operations that this entry can be used for. Attempting to use this access method for any other scope will lead to 403 error rejections. Scopes can contain plugin IDs and/or permissions. + +Introducing a new external caller with its exclusive secret key entails updating the app-config.yaml file and restarting the backend. This mechanism is designed to support integration with selected services, and therefore dynamic addition of callers is not necessary at this point. + +### Scopes + +Scopes are optional. If you do not specify any scopes, the access method in question has unlimited scope and can perform all types of action. + +In the examples below, either a string or a list of strings can be given. + +If _any_ scope rule matches, the action is permitted. I.e., if you specify a plugin rule, then also adding permission rules for that plugin will have no effect since the plugin rule will have already matched. + +You can limit by the target plugin ID being accessed with the token: + +```yaml +scope: + plugin: catalog +``` + +You can limit by the type of permission being requested: + +```yaml +scope: + permission: catalog.entity.read +``` + +Or by permissions attribute: + +```yaml +scope: + permissionAttributes: { action: read } +``` + +### The Legacy Access Type + +Configuration example: + +```yaml +backend: + auth: + externalAccess: + - type: legacy + options: + secret: ${EXTERNAL_ACCESS_SIGNATURE_SECRET} + # scope: ... +``` + +This corresponds exactly to the old shared secret signature method, and any secrets entered here will be merged with those specified under `backend.auth.keys` if any. + +### The Static Access Type + +Configuration example: + +```yaml +backend: + auth: + externalAccess: + - type: static + options: + token: ${SERVICE_API_TOKEN} + # scope: ... +``` + +This allows you to specify any static token string as an API key, which callers send verbatim as the header `Authorization: Bearer <token>`. + +### Implementation + +The token verification of the two access methods will live entirely with the `coreServices.auth` service implementation, and will be returning regular credentials with a service principal. The implementation will only need to alter the `authenticate` method and no additions to the API will be necessary. + +The service principal type will need to be amended with an optional scope field, carried over from the configuration. This will let the `ServerPermissionClient` compare against the set of allowlisted actions. In addition, the auth service can early-reject based on plugin ID if such rules are given. ## Release Plan @@ -117,13 +171,30 @@ This section should describe the rollout process for any new features. It must t If there is any particular feedback to be gathered during the rollout, this should be described here as well. --> -TBD +The initial pilot implementation of the `legacy` and `static` types has already been merged, along with the corresponding config additions. -- adding different types in the future +Validating this implementation, and adding scope restrictions, is still on todo but could be added incrementally at any time without breaking changes - as long as the default scope is "all". + +We have left it out of the scope of this BEP to allow for addition of more access types. Making that a possibility will likely require the addition of service extension points at a framework level, which is not yet made a priority. + +Potential _future_ access types (not part of this BEP, but rather only for illustration) could for example be: + +```yaml +backend: + auth: + externalAccess: + - type: jwks + options: + url: https://other-service.acme.org/.well-known/jwks.json + - type: certificate + options: + publicCert: + $file: ./service-cert.pem +``` ## Dependencies -- 0003-auth-architecture-evolution +- [BEP-0003: Auth Architecture Evolution](https://github.com/backstage/backstage/tree/master/beps/0003-auth-architecture-evolution) ## Alternatives diff --git a/contrib/docker/minimal-harded-image/README.md b/contrib/docker/minimal-harded-image/README.md deleted file mode 100644 index accb2188ac..0000000000 --- a/contrib/docker/minimal-harded-image/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Minimal Hardened Image for Backstage - -DockerHub images in general did not seem ideal for Backstage as the number of vulnerabilities were quite high regardless of the image used. - -The `Dockerfile` in this directory uses a [`wolfi-base`](https://github.com/wolfi-dev) image from Chainguard Images. This improves the security of the application and reduces false positives in scanners. - -## Considerations - -- `Wolfi` only releases the `latest` tag for public consumption however digests can be pinned. -- `Wolfi` OS uses packages from the [os repository](https://github.com/wolfi-dev/os) on GitHub. Some packages may be named differently. -- While `Wolfi` uses `apk`, the OS is designed to support `glibc`. -- Due to the stripped down nature of the base image, additional packages might be needed compared to a distribution like Debian or Ubuntu. diff --git a/contrib/docker/minimal-harded-image/Dockerfile b/contrib/docker/minimal-hardened-image/Dockerfile similarity index 100% rename from contrib/docker/minimal-harded-image/Dockerfile rename to contrib/docker/minimal-hardened-image/Dockerfile diff --git a/contrib/docker/minimal-hardened-image/README.md b/contrib/docker/minimal-hardened-image/README.md new file mode 100644 index 0000000000..62045a4ded --- /dev/null +++ b/contrib/docker/minimal-hardened-image/README.md @@ -0,0 +1,25 @@ +# Minimal Hardened Image for Backstage + +DockerHub images in general did not seem ideal for Backstage as the number of vulnerabilities were quite high regardless of the image used. + +The `Dockerfile` in this directory uses a [wolfi-base](https://github.com/wolfi-dev) image from Chainguard Images. This improves the security of the application and reduces false positives in scanners. + +## Pinning Digest + +To reduce maintenance, the digest of the image has been removed from the `./Dockerfile` file. A complete example with the digest would be `cgr.dev/chainguard/wolfi-base:latest@sha256:3d6dece13cdb5546cd03b20e14f9af354bc1a56ab5a7b47dca3e6c1557211fcf` and it is suggested to update the `FROM` line in the `Dockerfile` to use a digest. + +Using the digest allows tools such as Dependabot or Renovate to know exactly which image digest is being utilized and allows for Pull Requests to be triggered when a new digest is available. It is suggested to setup Dependabot/Renovate or a similar tool to ensure the image is kept up to date so that vulnerability fixes that have been addressed are pulled in frequently. + +### Obtaining Digest + +To obtain the latest digest, perform a `docker pull` on the image to get the latest digest in the command output or use `crane digest <image>`. + +## Considerations + +- Wolfi only releases the `latest` tag for public consumption however digests can be pinned. +- Wolfi OS uses packages from the [os repository](https://github.com/wolfi-dev/os) on GitHub. + - Some packages may be named differently. +- While Wolfi uses `apk`, the OS is designed to support `glibc`. +- Due to the stripped down nature of the base image, additional packages might be needed compared to a distribution like Debian or Ubuntu. +- Chainguard will maintain one version of each Wolfi package at a time, which will track the latest version of the upstream software in the package. Chainguard will end patch support for previous versions of packages in Wolfi. [Read more here](https://edu.chainguard.dev/chainguard/chainguard-images/faq/#what-packages-are-available-in-chainguard-images) + - It is encouraged to use a relative pin or use a third-party tool to ensure the packages are kept up to date diff --git a/contrib/docs/tutorials/authenticate-api-requests.md b/contrib/docs/tutorials/authenticate-api-requests.md index 8b71151ee9..838aaffef6 100644 --- a/contrib/docs/tutorials/authenticate-api-requests.md +++ b/contrib/docs/tutorials/authenticate-api-requests.md @@ -1,5 +1,14 @@ # Authenticate API requests +> [!CAUTION] +> This entire guide MUST NOT BE USED by users of Backstage 1.26 and +> newer. If you have applied the changes in this guide, you need to remove them +> again as you upgrade to recent versions of Backstage. When [the new auth +> changes](https://github.com/backstage/backstage/tree/master/beps/0003-auth-architecture-evolution) +> landed backends became natively secured through the framework, and the +> instructions outlined in here can interfere with the backend functioning +> correctly. + The Backstage backend APIs are by default available without authentication. To avoid evil-doers from accessing or modifying data, one might use a network protection mechanism such as a firewall or an authenticating reverse proxy. For Backstage instances that are available on the Internet one can instead use the experimental IdentityClient as outlined below. API requests from frontend plugins include an authorization header with a Backstage identity token acquired when the user logs in. By adding a middleware that verifies said token to be valid and signed by Backstage, non-authenticated requests can be blocked with a 401 Unauthorized response. diff --git a/docs/assets/architecture-overview/package-architecture.drawio.svg b/docs/assets/architecture-overview/package-architecture.drawio.svg index df8c0f8820..89fab2647d 100644 --- a/docs/assets/architecture-overview/package-architecture.drawio.svg +++ b/docs/assets/architecture-overview/package-architecture.drawio.svg @@ -1,69 +1,66 @@ -<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1261px" height="642px" viewBox="-0.5 -0.5 1261 642" content="<mxfile><diagram id="M4OCM2KiCGRnt6vHj1W_" name="Page-1">7V1dl5s2E/41vnSO+bR9mXV227SbdpNsT5v35j0yyDYNIB8s73rz6ysw2IAEyHxKjnNOT40MXph5ZuYZaUaMtIV3+CUA280nZEN3pE7sw0j7MFJVZWJMyf/CkbfjyEyfHwfWgWPHJ50Hvjo/YHJlPLp3bLjLnIgRcrGzzQ5ayPehhTNjIAjQa/a0FXKzf3UL1pAa+GoBlx7927HxJh5VZ5PzF79CZ72J/7Rpxl8sgfV9HaC9H/+9kaqton/Hrz2Q/FZ8/m4DbPSaGtLuR9oiQAgfP3mHBXRD2SZiO173UPDt6b4D6GOeC9TjBS/A3cPkjqP7wm+JLKKngeH5k5F297pxMPy6BVb47SvRPhnbYM8lRwr5uMMB+g4XyEUBGfGRT067i/8GDDA8FN6ncnp6giqIPIiDN3LKIQuMt+zha0o58dAmpZZkDMRwWJ9+9ywS8iGWCltCBxf+Zh908/7vL9aXp3t19/QvHiuG1rLggOusffLZhSty83ehsByCx/fxMEbh+SvHdZmypQTJEHexbOdZ4RpM6bLEq3Ul37lOSRPaxELjw/jZswJGAd6gNfKB+4hCaUVi/Rdi/Bb7F7DHKCt0eHDwP6nP38KfemfERx8O8S9HB2/JgU8e8J/0Qeqq8PB8WXSUXLdCPk50R7zCJPoXnWW/Dz0WGV66yPr+vHH84/CD457NCgQ4fgz9+G3qqBADO7QPrFh+f32GD+jtsPzw6XGGXha//fi41saJbwXBGuKS82K0hzooRVQAXYCdl6wXbd/6EoAWwOOMhPvzaHtoOSPkWxogTLR0qHVrH7xEj6O0DQH7r8Ni/ON39eO3F/+PPfhswx/fxqopGAZoh1uNgYzMOgKEUgqIa3cfTOxMp2JhJ8GyENiZ8nuTIvBMrwU9THVN1aHQU3bbKeYHtttm5G8LA4fcHAzCUxx/HV+VJ4GeY9sRFHNkcXe86jE6+jA7j5AjNUcbifZtAGcriyLr5BvTmsHl6vRNkvqUBhh+npmjmapO08wZg2V2RjIVRRPIDTT3AlI6AZUzhBi6YCFk1oiCKv0lLLKonXneYMThz+fPC/1/c2f+5Y8naxE8m9PJ58T11/cX7REFXpb5TjUyXoLDRzwAz3HDgWewQR6IR+MbVBSJIMX0JIM5krK7TrGJcAqRyEQmRrFarVSLyShsc2kaZk+MQp8MzSgYs4CSJRY5f6HIyCk04RMLNnq0xmnpwHOgqpQUlBcuhmhwmQoDF5UXLzXoiLR4UQabMS277RTT2Lr7teOPR6oJvJA/uGHojwfJTyXDa3wSlxx05OIJDi3huPHjqSXQa4G2qNPqmRBl2u9USGNf0hVx+Ymm06eiZzAFcah8sbbPODStGYekpLkMuAgWhwrwYgiDl3o0V87lFl64DEdzy267Jm0Z/wyzKQPTF9a0S2f0pSwgpjByl6h98ugsAxA4cEdBYPfqeC6IKonSNjdJKdAiUoIBQ9XHeqwWhGnkKtv0OS3MU7VgpvjK6EqaBiXNkR4VNhIpEZegPsQ2NbaQ5yH/ykyL1mkp5PitJqtnjWEzXc1VMh/A5NUyhjs83mPHpS3opmkOTU+H1jQdQYs0DXbfb0qupWSFVTPbq5ZnlJYXsXsWPQQqEz03H2JyxsCTbbUuzXmVzVgAAxetx5brhE8ukdHYBpzZOstoZupSMy8wmtmlms4bDYPr9Gs0idUW6xm/bRl2c1OvDBRHoUv18laM/JWzvqlXSl6TVIAXqxcGAQpu1ispo1EYPVbsIOwdGxBvaq6hZmNoNbNa6bJqdggDXQcAO3JNNoij5OFDsUpPNzwEiOg1mqN7HxbQTxYogJR+hchQqokNe4qujf5TtjjpbI9iNgEcg+2W/OdIZTTVa/ecRnOCnLT8Va3MQkMF23AF9i6Wi+aIo+U+aWxB7QWluV6bWS9aESUHTwlSkrHM+iRj9bJ6yfPyVlb2JB6jMqssnoqyIK7eANAdANgiHwoApbddtpnEbgO24UciKfftLghn7HG1V882HK1cZ/vr6IL9JnYx2YpA0QKPms9yvJRBpBiu15h353vnNWyvm7q3n6IypbS3T+DSlIJKpjqee+DKtyxghK/Y5waMWJWSpbfdsJYpnO7auzB3UTwoc9226IVPiplLEBl12323sNdpQxY3fsnpjqbil1YWJB6D7qN0UQtRXxsppUrEdd1Mo7OqRjw6yudGPYBquC7G0vuuG+SuNbjVakrakSzP2sQ3tnbBbhd/Pu0bmdjGUCGQUfvbcwik4Haqe3pGyA31IeSaAlX1pDBmIpnpcBtFT0xRVq+3ulKtJUDFNuCUZXJzc6oB9gJcKchkWEooCPWi7nXE2d93ir5y9PeVNmKJm7YWYKdxg9+w+2RdK3YEo4Ol950KKvcHEgCI8snoU0QCR8KXKE+N3MQ1b4VyK106BVtQ8/FshDcwGBex7QACS67q5Torx+XIvICyVQV1lbVNdndBnXM+sRwC8nVx1Sqs6gYDLM7eLwY4t/Uox4CPbLr0SmAE1Gr86QYBrDrZfiFgDlpActm07wC7npfOxEq3ecKkzoL1IKmbUFvfc4NAinys8QYaA+9bnKpTkGH3lWYeRLCELLnvFGGQJQsztOosjDmt1koWxt4Mnm4gvGi14xrzrzbX6Msh3GLmdlrYaJuyMWFDF3ZfhJqrTNnEhw2rVLxX2NBdHxfB5gqzPPFBw+yj7Ao1pQSktLy4OL5nAZJZeM6sSKeXqm2w21QT9AuW03LLzozCYZ0Z+DvbIEIvbWg7MareetrKNV8bvINvRqXzdbrFLu+nbXbTpW9202lOlN+KQ8qNqATS8eA7Nkwrt5az4ctNxXLv2kAvSqUCY78zDC0FRdUYMCoyE4HKzftSWyZc5RwDpz0pUu3rxlR1pcuM+I+FvC3yiTCuzXHy5Rt9wWFwlqTS3jXPkjbQkyrLF8jah98LQONoBV4TZW5pkRQl1erEQr4PLQyWyS9MLhEK83XwrAmMVl4Hz96NkeEEGyGcq/s5jdP4jfOcFOOsxmokDifVGSPjuna/0Vhd/PMkDF2qnRkIvQwlkypbLOdprEpmRVefqmRsWimRKluszWusSlZ7bq+qVClVpueICWmLKFtOueRZcVaDWVHGoYgRnSqi2knDLMBkIZWZv6cR0J0GtUq/qvSrQpqPfdwhDwXbjWPdlMilRNbbzfpVYtn7QW4arNYgKy/qSoPMR5CbqbbYh9gCU+0vKBZUsFK6XDx+vBkiZzwc2hIVXe5Uo1bWWKP9NzeHwthluWe1yZ1W1MoQG6vN7DGvL1AbnULIpLZa2WBza5sNrjaOaVyR1daPkzSFc5ImpSUx30waFjU+AM9xw4FnsEEeiEfjP6MoI0H7Jk4Eorr1KrGiys6JBG7zHKDa2H+94NYY63ASGXhP4dQULpw23u71ZuDVBq5wGzhvY90ABt72kuNVEq98BB+eeE0lMfBGO2BK6xn4Q78hrGdIWKoAEKvYM7PRLuHSYow/+oiLMb3xtr4Du7GrRph6BQgz6YmlBfK2ADtLx3XwG4U/Cabh061/kxboDaUORtuZwtrqpTt+Y3KshCUCfgRL6D6hnRO9OI+YCsIYeQwNHMvd0pqNXzbiHdZEDZt3S7BzrHdnl/J/HDjAXzP2FuEr1+Wz/uLVsmzXp0LT39OKW4AwiB9/HJZytkR7lUram7xYJFPKN28Oi7YnFplGXCAQhth4lx1NVhEAq9qxBctpe2qmMxFR8yM9Cqnt9LY3IbFKgtqR0SiszkI49d0voff7FL1cVrv/Dw==</diagram></mxfile>" style="background-color: rgb(255, 255, 255);"> +<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1261px" height="811px" viewBox="-0.5 -0.5 1261 811" content="<mxfile><diagram id="M4OCM2KiCGRnt6vHj1W_" name="Page-1">7V1bk+K2Ev41PDKFr8Dj7uxMsjmzqUl2U8k+nRK2AGcNooyYYfbXRzaysa32Bd/FsFWpYHEZu79PfZG6WyPtfnP8xUO79RdiY3ekTuzjSPs0UlXFmM/Z//yRNz6iGbPTyMpzbD52Hvjq/MR8cMJHD46N94kPUkJc6uySgxbZbrFFE2PI88hr8mNL4ib/6g6tsDDw1UKuOPq3Y9M1H1Vnk/Mbv2JnteZ/2jT5Gwtk/Vh55LDlf2+kasvg3+ntDQp/i39+v0Y2eY0NaQ8j7d4jhJ5ebY732PWFG4rt9L3HjHej+/bwlpb5wtHFv9lH3Xz4+0/rz+cHdf/8Lx0rhnb6nRfkHnD4IMHt0rdQRMFDYv9nJiPt4+vaofjrDln+u6+MFWxsTTcuu1LYS+Q6qy177eIlu62PL9ijDpP2Bz5Mif/5peO698QlHhvZki37pY/i4/An9H8BH2ND/PF+wWSDqffGPhK+O+ei5lw0QwxfY8BO+Ng6hqmi8UHEybSKfvssUPaCy/QC+c51QZrYZvzjl/zZkwImHl2TFdki94n40grE+i+m9I3PHnSgJCl0fHToP7HX3/2fujP41acj/+Xg4i282LIH/Cd+EfuWf3n+WnAVfm9JtjTEjnF+EvwLPmV/8OcjG164xPrxbe1sT8OPjhve6J4ij/LH0E/vxq4yObAnB8/i8vvrD/xI3o6LT1+eZuTl/refn1faONQcyFthmvM5znYfg1xGedhF1HlJ6ojG2aGESjCDHmcmPJxHm2PLmSHf4wQB2dIi6tbBewkeR2maAvZfx/vxz/+pn7+/bH8/oD9s/PP7WDUHxgFR4RZzICGzlgih5BLi2tUHyJ3pdFjcCbk8CO5My2uTLPJMr4U9IFxTtS/25N12zPNDu10952+HPYfdHPb8jzjbFf9W2gncOLYdUDHlLO5P33oKrj7NziPsSk25jQx9G+HZ0gqA9cgPHHvHtGZ4sYzeCR37XANT3s9MuZmqLrqZM8DLbM3JVBRtQGqgvhaQUgmoJU2IoQ/MhMxquaBKdwGLLLCDn+vNcci765jq91czmExkUv/L5VK1QPVvmwvTMDtS//qkb/UPLNlI5gXeqZHOjyIP2QyANngvEGaPVjuG6HnBSpXSXyhLF2NodJkOhi5qWb6k1Mvkmvmi9La8lXfbMU9j5x5WznY8Uk208f0H1zf9fJD9VDi8opG45HBHLo5GNU61cOqoOdRrwG1Rp8VhqzLtNm6trUvaclzy1z5bVBCVlVZ1zTIVNYvaeJDKv/pMHHZ7ESe1FCf1aYprp5vi30rRLbqNOtYsf3+uS2s2rWjNpHSWAc4NzJpl8MUYDF+qOctyrrCXpUt/znLebVd0fsbvYU2mZycIWrxpzQkC14NnAkfuyWZDtmzsyVl4yHPwXmDA/tXZuCjIHYlPuUkMP4sJCXsA0qcMnAZkqUz0lEdpisKM0p/iwlTTZr4xac4FaY70IFOLiYmpBPXRQhS5ZDW2XAdzn0KSqWUbeGbr0NSaqQvNNEFQcylXftYkcY7yrOKTpq0lT/AJwryZbJzp2w6YNzd4S8Cr9Y6uuDOdnsVku3RWN3irwDvtHV5x9ykFL2aOpnebvdWUs9I7vkBKMWyEN6d08hvMFWA2+oYZyhxPwuwwD3TlsTjL92ZvIF8Ocv+mOEwwjIH86LHwLAhLJx/8fLHJPfGwgO8gIpRixwYMTyIV2rw4xWhP8Gw8PEa7HfvPkWrSFO9+lJw0EeWk9V/VwijUB9jGS3RwqVxuznBQ7tKNzdi9EpDrtHbjotVgdvEcMiUcS6zNAiu3xcu9l1dugFzQgb3tPHs6lM0A9UaA9ggAi7wvAuTedl7t5H6Ndv5LJin37aPnr+jTYq2ezK9dus7u19EF5ZV77mwFpGjAj5rPUn4p4EgBqteYt6d75xXmXjuZA+9iVy43lX3A23IZu7hVNHcvu/7dZJroapyO48ndZKIXEDK4SluUZqnUVRKKbia1mz5Lqa3mklByn73mNrG/mnZwcepLfFDmxLqh7ykrKf5oQGJd1wVhVYp6hmseB5/hnVssLJ15jDqW9BLYXJTj3VVbgrix1M04O4vS7xo2lKVJ1V+ZSe59VzVy12rcKmWN71kQaa35ja1ctN/z11GPoXBu9GUCgbSqjk2gQLcoreobIa6PxyC3LISkKgVY6ASj7SZyqkBRFm/nulJtVWDFNvAUmnJzc6oheH8vl2Qy7FRkmPqhdg4o2Xwmsr5ydA7IzXFvN/atyZPadRL9dpi4Vp4MzPXLve+YAXk4MmXPwGejz4HDNxp8tvPUSK2Bl012jjJ1mhesUc6nJnSNvXGWZ+1hZMmVCF1lEzqfmRe4Z0UGXIUaTLZnwEuuHeZTwOKusUQcqJKj1Q4HIP+8Ww6UrLHO58CW2GIW14AZUGJVuCsGQCm33VLA7DUX5bIl3h76heauukpXgzqpsvfdS5jWw+qsORf3MucF3n71JdrSxBpcPFe7jrnnjoGxlAkZiuDraaCBBXThfcccDlmiOEMrjuLAJbhGoji4DatYy3jRzsg1xm9N7ufnU7jByC/aBGna5QNpI+aYX8Saqwz5hk8bKGu9U9qIBSgX0eYKo8ThkwYs6WyLNbkOSG6mc7Z9TxIksUmd2L2Ob2vbaL8ujvIu2HpLbVEDOcw6aPhb61Wh59bWRR5VZ+V1+chXJm+Xy+SwnMsV3XGV927r7nTp6+70wro7ivd0fKCO+06r7mpj3HvziKm455XC2MYvN4jlbiAhbmrFDGO3KwwNGUXV6NEqgoGA6Hxkd2+4yjWGkvMpIqMU8wmEulBlBv6PRTY7smXCuDbFWS7e6IoOvXtJqqhd017SGm+kivIHNNv7b0uglahK9s+t3YkiyQqqo9N30SL8hcklQgEPYoUWMBo5iBVuDAkowVoMb+6c2wIYi5nYn1RnQMR17XqjNlzl10kALNXWJoi4DSUTlA2mA9WGEswI6xJKoH+mRFA2mNtXG0qolLdTKFUByvgaMXPaApctBS57VppEMClKbooA61Rg1SKEIcIkKZVYvxcZ0B6CWqFeVbqFUPTHPu/Jhni7tWPdQCwFInRUTbcgikHTx7A7/w3BEghCcVFbCIKPILen2mDNYgOeandGMSMDVsDy/unzbSKWtId9z0RFlzvUqBQ1VigVTinQkk3XWoRN7rCiUoRYHzZg6atj2MQQQibYKkWD9WED2qt3DFuJZdwhw9aNkjQHpyRlOU3ZT2p8RBvH9Qe+oTXZID7K/4yijAZaNxE5EMWlW+EsKqycCOmWOtzdTPcCbJApwD6cRBO8I3OanuD9m9PanWdvE7x4giulJ3jZwroeJnjTW45X6XilJ3j/jtdwDrhvsVumtJqhvOk3BqsZQo4PgGIF/TVrNSyXlmPlrc9wOabXbgHcsxq7aoapV8AwU1xYuiebHaLOwnEd+ibwT4Jl+Hjp36QB90aAAzo6F2oV055/Y5bYCQsF/IQW2H0meyc4w49NFUIp2QAInNLd4sjyc082xxWDYX23QHvHujurlP9Tz0HbFdCbpFy6brnZn71blqz6VET3N9px8whF/PHHfipnQ26vUuj2hmecJFL55vVp0fTCIjiJm5g7yakzgzI5oGzHBmZO05FbayJKh08dykgsIDlnRHRwMCSQpCyIsmx+stHpyZBiJ/RU/n14oIeEx0CWXhUsgM+8BL4uSyoKa4lD8GQ83rEX9LqsmFCqBN5X3TptBEUXhVENWGYGxDQZRKh5YJOSapGvposkT7cqHNhU+ENCtWVzJz9BVVcXmMaGOmjUM5pZ52TFZq4BWs0mWmiEx63CGZidNMsokN4J4kzxzYscNVVTANk1UQAcnlRabLXkbILRlN1SaldyztsyXIWHcIQIytnioh8EIdejLQS1wirss+coYQOLfgAEa+vbQhBut5FjlbrtVKFeXrpcvHgQuVbdtGoqHRpTtP9xnfOjuMHLxTAXmi1wjagRpVfYZSQElOzwlnkd71fxZWOdvfpnFFmz9oDt/zygLo5GDtqHp84O8i15QT/xKN6fXnikFdyEvK3oPCtgqRuep+NEPUW4suG5UAXY2MHMI7+KltD4x/1dqi/Exv4n/gM=</diagram></mxfile>" style="background-color: rgb(255, 255, 255);"> <defs/> <g> - <rect x="0" y="0" width="10" height="10" fill="#ffffff" stroke="none" pointer-events="all"/> - <rect x="180" y="500" width="100" height="130" fill="none" stroke="#000000" pointer-events="all"/> - <path d="M 160 245 L 253.88 245" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/> - <path d="M 258.88 245 L 253.88 246.67 L 253.88 243.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/> - <path d="M 120 230 Q 120 230 120 136.12" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/> - <path d="M 120 131.12 L 121.67 136.12 L 118.33 136.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/> - <path d="M 120 260 Q 120 305 143.88 305" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/> - <path d="M 148.88 305 L 143.88 306.67 L 143.88 303.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/> - <path d="M 140 230 Q 140 137.5 243.88 137.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/> - <path d="M 248.88 137.5 L 243.88 139.17 L 243.88 135.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/> + <rect x="180" y="670" width="100" height="130" fill="none" stroke="rgb(0, 0, 0)" pointer-events="all"/> + <path d="M 160 245 L 253.88 245" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/> + <path d="M 258.88 245 L 253.88 246.67 L 253.88 243.33 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/> + <path d="M 120 230 Q 120 230 120 136.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/> + <path d="M 120 131.12 L 121.67 136.12 L 118.33 136.12 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/> + <path d="M 120 260 Q 120 305 143.88 305" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/> + <path d="M 148.88 305 L 143.88 306.67 L 143.88 303.33 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/> + <path d="M 140 230 Q 140 137.5 243.88 137.5" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/> + <path d="M 248.88 137.5 L 243.88 139.17 L 243.88 135.83 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/> <rect x="80" y="230" width="80" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 70px; height: 1px; padding-top: 245px; margin-left: 90px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"> app </div> </div> </div> </foreignObject> - <text x="90" y="249" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="90" y="249" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> app </text> </switch> </g> - <path d="M 120 390 Q 120 345 143.88 345" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/> - <path d="M 148.88 345 L 143.88 346.67 L 143.88 343.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/> - <path d="M 160 405 L 253.88 405" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/> - <path d="M 258.88 405 L 253.88 406.67 L 253.88 403.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/> - <path d="M 140 420 Q 140 450 347.5 450 Q 555 450 555 473.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/> - <path d="M 555 478.88 L 553.33 473.88 L 556.67 473.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/> + <path d="M 120 390 Q 120 345 143.88 345" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/> + <path d="M 148.88 345 L 143.88 346.67 L 143.88 343.33 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/> + <path d="M 160 405 L 253.88 405" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/> + <path d="M 258.88 405 L 253.88 406.67 L 253.88 403.33 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/> <rect x="80" y="390" width="80" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="all"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 70px; height: 1px; padding-top: 405px; margin-left: 90px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"> backend </div> </div> </div> </foreignObject> - <text x="90" y="409" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="90" y="409" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> backend </text> </switch> </g> - <path d="M 387.5 230 Q 387.5 205 378.75 205 Q 370 205 370 186.12" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/> - <path d="M 370 181.12 L 371.67 186.12 L 368.33 186.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/> - <path d="M 430 245 Q 475 245 475 266.25 Q 475 287.5 513.88 287.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/> - <path d="M 518.88 287.5 L 513.88 289.17 L 513.88 285.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/> - <path d="M 430 237.5 Q 585 237.5 585 233.75 Q 585 230 690 230 Q 795 230 795 243.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/> - <path d="M 795 248.88 L 793.33 243.88 L 796.67 243.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/> + <path d="M 387.5 230 Q 387.5 205 378.75 205 Q 370 205 370 186.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/> + <path d="M 370 181.12 L 371.67 186.12 L 368.33 186.12 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/> + <path d="M 430 245 Q 475 245 475 266.25 Q 475 287.5 513.88 287.5" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/> + <path d="M 518.88 287.5 L 513.88 289.17 L 513.88 285.83 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/> + <path d="M 430 237.5 Q 585 237.5 585 233.75 Q 585 230 690 230 Q 795 230 795 243.88" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/> + <path d="M 795 248.88 L 793.33 243.88 L 796.67 243.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/> <rect x="260" y="230" width="170" height="30" fill="#dae8fc" stroke="#6c8ebf" stroke-width="3" pointer-events="all"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 160px; height: 1px; padding-top: 245px; margin-left: 270px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; font-style: italic; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-style: italic; white-space: normal; overflow-wrap: normal;"> plugin-<plugin-id> </div> </div> @@ -74,19 +71,19 @@ </text> </switch> </g> - <path d="M 387.5 420 Q 387.5 555 493.88 555" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/> - <path d="M 498.88 555 L 493.88 556.67 L 493.88 553.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/> - <path d="M 430 412.5 Q 585 412.5 585 416.25 Q 585 420 690 420 Q 795 420 795 406.12" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/> - <path d="M 795 401.12 L 796.67 406.12 L 793.33 406.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/> - <path d="M 430 405 Q 475 405 475 383.75 Q 475 362.5 513.88 362.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/> - <path d="M 518.88 362.5 L 513.88 364.17 L 513.88 360.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/> + <path d="M 387.5 420 Q 387.5 445 383.75 445 Q 380 445 380 463.88" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/> + <path d="M 380 468.88 L 378.33 463.88 L 381.67 463.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/> + <path d="M 430 412.5 Q 585 412.5 585 416.25 Q 585 420 690 420 Q 795 420 795 406.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/> + <path d="M 795 401.12 L 796.67 406.12 L 793.33 406.12 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/> + <path d="M 430 405 Q 475 405 475 383.75 Q 475 362.5 513.88 362.5" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/> + <path d="M 518.88 362.5 L 513.88 364.17 L 513.88 360.83 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/> <rect x="260" y="390" width="170" height="30" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 160px; height: 1px; padding-top: 405px; margin-left: 270px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; font-style: italic; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-style: italic; white-space: normal; overflow-wrap: normal;"> plugin-<plugin-id>-backend </div> </div> @@ -97,69 +94,9 @@ </text> </switch> </g> - <path d="M 500 480 L 500 480 L 720 480 L 720 480" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/> - <path d="M 500 480 L 500 630 L 720 630 L 720 480" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 500 480 L 720 480" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> - <text x="609.5" y="497.5"> - Backend Libraries - </text> - </g> - <rect x="520" y="510" width="180" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> - <g transform="translate(-0.5 -0.5)"> - <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> - <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 525px; margin-left: 530px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> - @backstage/backend-common - </div> - </div> - </div> - </foreignObject> - <text x="530" y="529" fill="#000000" font-family="Helvetica" font-size="12px"> - @backstage/backend-common - </text> - </switch> - </g> - <rect x="520" y="550" width="180" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> - <g transform="translate(-0.5 -0.5)"> - <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> - <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 565px; margin-left: 530px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> - @backstage/backend-test-utils - </div> - </div> - </div> - </foreignObject> - <text x="530" y="569" fill="#000000" font-family="Helvetica" font-size="12px"> - @backstage/backend-test-utils - </text> - </switch> - </g> - <rect x="520" y="590" width="180" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> - <g transform="translate(-0.5 -0.5)"> - <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> - <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 605px; margin-left: 530px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> - @backstage/backend-tasks - </div> - </div> - </div> - </foreignObject> - <text x="530" y="609" fill="#000000" font-family="Helvetica" font-size="12px"> - @backstage/backend-tasks - </text> - </switch> - </g> - <path d="M 1030 250 L 1030 250 L 1250 250 L 1250 250" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 1030 250 L 1030 520 L 1250 520 L 1250 250" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 1030 250 L 1250 250" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> + <path d="M 1030 250 L 1030 250 L 1250 250 L 1250 250" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/> + <path d="M 1030 250 L 1030 520 L 1250 520 L 1250 250" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> <text x="1139.5" y="267.5"> Common Libraries </text> @@ -167,16 +104,16 @@ <rect x="1050" y="440" width="180" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 455px; margin-left: 1060px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/catalog-client </div> </div> </div> </foreignObject> - <text x="1060" y="459" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="1060" y="459" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/catalog-client </text> </switch> @@ -184,16 +121,16 @@ <rect x="1050" y="280" width="180" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 295px; margin-left: 1060px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/types </div> </div> </div> </foreignObject> - <text x="1060" y="299" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="1060" y="299" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/types </text> </switch> @@ -201,16 +138,16 @@ <rect x="1050" y="320" width="180" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 335px; margin-left: 1060px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/config </div> </div> </div> </foreignObject> - <text x="1060" y="339" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="1060" y="339" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/config </text> </switch> @@ -218,16 +155,16 @@ <rect x="1050" y="360" width="180" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 375px; margin-left: 1060px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/errors </div> </div> </div> </foreignObject> - <text x="1060" y="379" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="1060" y="379" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/errors </text> </switch> @@ -235,16 +172,16 @@ <rect x="1050" y="400" width="180" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 415px; margin-left: 1060px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/catalog-model </div> </div> </div> </foreignObject> - <text x="1060" y="419" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="1060" y="419" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/catalog-model </text> </switch> @@ -252,24 +189,23 @@ <rect x="1050" y="480" width="180" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 495px; margin-left: 1060px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/integration </div> </div> </div> </foreignObject> - <text x="1060" y="499" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="1060" y="499" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/integration </text> </switch> </g> - <path d="M 10 20 L 10 20 L 230 20 L 230 20" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 10 20 L 10 130 L 230 130 L 230 20" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 10 20 L 230 20" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> + <path d="M 10 20 L 10 20 L 230 20 L 230 20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 10 20 L 10 130 L 230 130 L 230 20" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> <text x="119.5" y="37.5"> Frontend App Core </text> @@ -277,16 +213,16 @@ <rect x="30" y="50" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 65px; margin-left: 40px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/core-app-api </div> </div> </div> </foreignObject> - <text x="40" y="69" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="40" y="69" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/core-app-api </text> </switch> @@ -294,36 +230,36 @@ <rect x="30" y="90" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 105px; margin-left: 40px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/app-defaults </div> </div> </div> </foreignObject> - <text x="40" y="109" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="40" y="109" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/app-defaults </text> </switch> </g> - <path d="M 988 305 Q 1010 305 1010 345 Q 1010 385 1023.88 385" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 1028.88 385 L 1023.88 386.67 L 1023.88 383.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 988 305 Q 1010 305 1010 250 Q 1010 195 1023.88 195" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 1028.88 195 L 1023.88 196.67 L 1023.88 193.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 990 10 L 985 10 Q 980 10 980 20 L 980 295 Q 980 305 975 305 L 972.5 305 Q 970 305 975 305 L 977.5 305 Q 980 305 980 315 L 980 590 Q 980 600 985 600 L 990 600" fill="none" stroke="#000000" stroke-miterlimit="10" transform="translate(980,0)scale(-1,1)translate(-980,0)" pointer-events="none"/> - <path d="M 430 345 Q 475 345 475 353.75 Q 475 362.5 513.88 362.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 518.88 362.5 L 513.88 364.17 L 513.88 360.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 430 352.5 Q 465 352.5 465 435 Q 465 517.5 493.88 517.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 498.88 517.5 L 493.88 519.17 L 493.88 515.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 988 305 Q 1010 305 1010 345 Q 1010 385 1023.88 385" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 1028.88 385 L 1023.88 386.67 L 1023.88 383.33 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 988 305 Q 1010 305 1010 250 Q 1010 195 1023.88 195" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 1028.88 195 L 1023.88 196.67 L 1023.88 193.33 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 990 10 L 985 10 Q 980 10 980 20 L 980 295 Q 980 305 975 305 L 972.5 305 Q 970 305 975 305 L 977.5 305 Q 980 305 980 315 L 980 590 Q 980 600 985 600 L 990 600" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(980,0)scale(-1,1)translate(-980,0)" pointer-events="none"/> + <path d="M 430 345 Q 475 345 475 353.75 Q 475 362.5 513.88 362.5" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 518.88 362.5 L 513.88 364.17 L 513.88 360.83 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 430 352.5 Q 468.4 352.5 468.4 463.2" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 468.4 468.2 L 466.73 463.2 L 470.07 463.2 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> <rect x="150" y="330" width="280" height="30" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 270px; height: 1px; padding-top: 345px; margin-left: 160px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; overflow-wrap: normal;"> plugin-<plugin-id>-backend-module-<module-id> </div> </div> @@ -334,17 +270,17 @@ </text> </switch> </g> - <path d="M 430 305 Q 475 305 475 296.25 Q 475 287.5 513.88 287.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 518.88 287.5 L 513.88 289.17 L 513.88 285.83 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 430 297.5 Q 464.08 297.5 464.08 186.12" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 464.08 181.12 L 465.75 186.12 L 462.41 186.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 430 305 Q 475 305 475 296.25 Q 475 287.5 513.88 287.5" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 518.88 287.5 L 513.88 289.17 L 513.88 285.83 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 430 297.5 Q 464.1 297.5 464.08 186.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 464.08 181.12 L 465.75 186.12 L 462.41 186.12 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> <rect x="150" y="290" width="280" height="30" fill="#dae8fc" stroke="#6c8ebf" stroke-width="3" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 270px; height: 1px; padding-top: 305px; margin-left: 160px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; overflow-wrap: normal;"> plugin-<plugin-id>-module-<module-id> </div> </div> @@ -355,10 +291,9 @@ </text> </switch> </g> - <path d="M 1030 160 L 1030 160 L 1250 160 L 1250 160" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 1030 160 L 1030 230 L 1250 230 L 1250 160" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 1030 160 L 1250 160" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> + <path d="M 1030 160 L 1030 160 L 1250 160 L 1250 160" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 1030 160 L 1030 230 L 1250 230 L 1250 160" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> <text x="1139.5" y="177.5"> Common Tooling </text> @@ -366,28 +301,27 @@ <rect x="1050" y="190" width="180" height="30" fill="#e1d5e7" stroke="#9673a6" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 205px; margin-left: 1060px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/cli </div> </div> </div> </foreignObject> - <text x="1060" y="209" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="1060" y="209" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/cli </text> </switch> </g> - <path d="M 850 400 Q 850 555 726.12 555" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 721.12 555 L 726.12 553.33 L 726.12 556.67 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 850 250 Q 850 95 736.12 95" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 731.12 95 L 736.12 93.33 L 736.12 96.67 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 740 250 L 740 250 L 960 250 L 960 250" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 740 250 L 740 400 L 960 400 L 960 250" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 740 250 L 960 250" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> + <path d="M 850 400 Q 850 555 776.12 555" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 771.12 555 L 776.12 553.33 L 776.12 556.67 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 850 250 Q 850 95 736.12 95" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 731.12 95 L 736.12 93.33 L 736.12 96.67 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 740 250 L 740 250 L 960 250 L 960 250" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 740 250 L 740 400 L 960 400 L 960 250" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> <text x="849.5" y="267.5"> External Plugin Libraries </text> @@ -395,16 +329,16 @@ <rect x="750" y="280" width="200" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 190px; height: 1px; padding-top: 295px; margin-left: 760px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> plugin-<other-plugin-id>-react </div> </div> </div> </foreignObject> - <text x="760" y="299" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="760" y="299" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> plugin-<other-plugin-id>-react </text> </switch> @@ -412,16 +346,16 @@ <rect x="750" y="320" width="200" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 190px; height: 1px; padding-top: 335px; margin-left: 760px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> plugin-<other-plugin-id>-common </div> </div> </div> </foreignObject> - <text x="760" y="339" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="760" y="339" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> plugin-<other-plugin-id>-common </text> </switch> @@ -429,30 +363,29 @@ <rect x="750" y="360" width="200" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 190px; height: 1px; padding-top: 375px; margin-left: 760px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> plugin-<other-plugin-id>-node </div> </div> </div> </foreignObject> - <text x="760" y="379" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="760" y="379" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> plugin-<other-plugin-id>-node </text> </switch> </g> - <path d="M 700 325 Q 700 325 733.88 325" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 738.88 325 L 733.88 326.67 L 733.88 323.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 610 400 Q 610 400 610 473.88" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 610 478.88 L 608.33 473.88 L 611.67 473.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 610 250 Q 610 250 610 186.12" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 610 181.12 L 611.67 186.12 L 608.33 186.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 520 250 L 520 250 L 700 250 L 700 250" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 520 250 L 520 400 L 700 400 L 700 250" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 520 250 L 700 250" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> + <path d="M 700 325 Q 700 325 733.88 325" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 738.88 325 L 733.88 326.67 L 733.88 323.33 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 610 400 Q 610 435 609.87 462.35" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 609.85 467.35 L 608.2 462.34 L 611.54 462.36 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 610 250 Q 610 250 610 186.12" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 610 181.12 L 611.67 186.12 L 608.33 186.12 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 520 250 L 520 250 L 700 250 L 700 250" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 520 250 L 520 400 L 700 400 L 700 250" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> <text x="609.5" y="267.5"> Plugin Libraries </text> @@ -460,10 +393,10 @@ <rect x="530" y="280" width="160" height="30" fill="#dae8fc" stroke="#6c8ebf" stroke-width="3" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 295px; margin-left: 540px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; overflow-wrap: normal;"> plugin-<plugin-id>-react </div> </div> @@ -477,10 +410,10 @@ <rect x="530" y="320" width="160" height="30" fill="#d5e8d4" stroke="#82b366" stroke-width="3" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 335px; margin-left: 540px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; overflow-wrap: normal;"> plugin-<plugin-id>-common </div> </div> @@ -494,10 +427,10 @@ <rect x="530" y="360" width="160" height="30" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 150px; height: 1px; padding-top: 375px; margin-left: 540px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; font-style: italic; white-space: normal; overflow-wrap: normal;"> plugin-<plugin-id>-node </div> </div> @@ -508,13 +441,11 @@ </text> </switch> </g> - <path d="M 250 10 L 250 10 L 730 10 L 730 10" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/> - <path d="M 250 10 L 250 180 L 730 180 L 730 10" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/> - <path d="M 250 10 L 730 10" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/> - <path d="M 260 20 L 260 20 L 480 20 L 480 20" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 260 20 L 260 170 L 480 170 L 480 20" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 260 20 L 480 20" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> + <path d="M 250 10 L 250 10 L 730 10 L 730 10" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/> + <path d="M 250 10 L 250 180 L 730 180 L 730 10" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/> + <path d="M 260 20 L 260 20 L 480 20 L 480 20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 260 20 L 260 170 L 480 170 L 480 20" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> <text x="369.5" y="37.5"> Frontend Plugin Core </text> @@ -522,16 +453,16 @@ <rect x="280" y="50" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 65px; margin-left: 290px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/core-plugin-api </div> </div> </div> </foreignObject> - <text x="290" y="69" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="290" y="69" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/core-plugin-api </text> </switch> @@ -539,16 +470,16 @@ <rect x="280" y="90" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 105px; margin-left: 290px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/test-utils </div> </div> </div> </foreignObject> - <text x="290" y="109" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="290" y="109" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/test-utils </text> </switch> @@ -556,24 +487,23 @@ <rect x="280" y="130" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 145px; margin-left: 290px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/dev-utils </div> </div> </div> </foreignObject> - <text x="290" y="149" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="290" y="149" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/dev-utils </text> </switch> </g> - <path d="M 500 20 L 500 20 L 720 20 L 720 20" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 500 20 L 500 170 L 720 170 L 720 20" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 500 20 L 720 20" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> + <path d="M 500 20 L 500 20 L 720 20 L 720 20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 500 20 L 500 170 L 720 170 L 720 20" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> <text x="609.5" y="37.5"> Frontend Libraries </text> @@ -581,16 +511,16 @@ <rect x="520" y="130" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 145px; margin-left: 530px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/integration-react </div> </div> </div> </foreignObject> - <text x="530" y="149" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="530" y="149" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/integration-react </text> </switch> @@ -598,16 +528,16 @@ <rect x="520" y="50" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 65px; margin-left: 530px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/core-components </div> </div> </div> </foreignObject> - <text x="530" y="69" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="530" y="69" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/core-components </text> </switch> @@ -615,131 +545,276 @@ <rect x="520" y="90" width="180" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 105px; margin-left: 530px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> @backstage/theme </div> </div> </div> </foreignObject> - <text x="530" y="109" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="530" y="109" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> @backstage/theme </text> </switch> </g> - <rect x="10" y="500" width="160" height="130" fill="none" stroke="#000000" pointer-events="none"/> - <rect x="20" y="510" width="20" height="20" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> - <rect x="20" y="570" width="20" height="20" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> - <rect x="20" y="540" width="20" height="20" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/> + <rect x="10" y="670" width="160" height="130" fill="none" stroke="rgb(0, 0, 0)" pointer-events="none"/> + <rect x="20" y="680" width="20" height="20" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> + <rect x="20" y="740" width="20" height="20" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> + <rect x="20" y="710" width="20" height="20" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> - <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 110px; height: 1px; padding-top: 520px; margin-left: 50px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> + <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 110px; height: 1px; padding-top: 690px; margin-left: 50px;"> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> Frontend Package </div> </div> </div> </foreignObject> - <text x="50" y="524" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="50" y="694" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> Frontend Package </text> </switch> </g> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> - <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 110px; height: 1px; padding-top: 550px; margin-left: 50px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> + <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 110px; height: 1px; padding-top: 720px; margin-left: 50px;"> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> Isomorphic Package </div> </div> </div> </foreignObject> - <text x="50" y="554" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="50" y="724" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> Isomorphic Package </text> </switch> </g> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> - <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 110px; height: 1px; padding-top: 580px; margin-left: 50px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> + <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 110px; height: 1px; padding-top: 750px; margin-left: 50px;"> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> Backend Package </div> </div> </div> </foreignObject> - <text x="50" y="584" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="50" y="754" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> Backend Package </text> </switch> </g> - <rect x="20" y="600" width="20" height="20" fill="#e1d5e7" stroke="#9673a6" pointer-events="none"/> + <rect x="20" y="770" width="20" height="20" fill="#e1d5e7" stroke="#9673a6" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> - <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 110px; height: 1px; padding-top: 610px; margin-left: 50px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> + <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 110px; height: 1px; padding-top: 780px; margin-left: 50px;"> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> CLI Package </div> </div> </div> </foreignObject> - <text x="50" y="614" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="50" y="784" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> CLI Package </text> </switch> </g> - <rect x="190" y="540" width="20" height="20" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> - <rect x="190" y="600" width="20" height="20" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> - <rect x="190" y="570" width="20" height="20" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/> - <rect x="250" y="540" width="20" height="20" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> - <path d="M 210 550 Q 210 550 243.88 550" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 248.88 550 L 243.88 551.67 L 243.88 548.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <rect x="250" y="600" width="20" height="20" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> - <path d="M 210 610 Q 210 610 243.88 610" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 248.88 610 L 243.88 611.67 L 243.88 608.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <rect x="250" y="570" width="20" height="20" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/> - <path d="M 210 550 Q 230 550 230 562.5 Q 230 575 243.88 575" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 248.88 575 L 243.88 576.67 L 243.88 573.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 210 605 Q 230 605 230 595 Q 230 585 243.88 585" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 248.88 585 L 243.88 586.67 L 243.88 583.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 210 580 Q 210 580 243.88 580" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> - <path d="M 248.88 580 L 243.88 581.67 L 243.88 578.33 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/> + <rect x="190" y="710" width="20" height="20" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> + <rect x="190" y="770" width="20" height="20" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> + <rect x="190" y="740" width="20" height="20" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/> + <rect x="250" y="710" width="20" height="20" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/> + <path d="M 210 720 Q 210 720 243.88 720" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 248.88 720 L 243.88 721.67 L 243.88 718.33 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <rect x="250" y="770" width="20" height="20" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> + <path d="M 210 780 Q 210 780 243.88 780" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 248.88 780 L 243.88 781.67 L 243.88 778.33 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <rect x="250" y="740" width="20" height="20" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/> + <path d="M 210 720 Q 230 720 230 732.5 Q 230 745 243.88 745" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 248.88 745 L 243.88 746.67 L 243.88 743.33 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 210 775 Q 230 775 230 765 Q 230 755 243.88 755" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 248.88 755 L 243.88 756.67 L 243.88 753.33 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 210 750 Q 210 750 243.88 750" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 248.88 750 L 243.88 751.67 L 243.88 748.33 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> <g transform="translate(-0.5 -0.5)"> <switch> - <foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> - <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 90px; height: 1px; padding-top: 520px; margin-left: 190px;"> - <div style="box-sizing: border-box; font-size: 0; text-align: left; "> - <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: none; white-space: normal; word-wrap: normal; "> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> + <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 90px; height: 1px; padding-top: 690px; margin-left: 190px;"> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> Compatibility </div> </div> </div> </foreignObject> - <text x="190" y="524" fill="#000000" font-family="Helvetica" font-size="12px"> + <text x="190" y="694" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> Compatibility </text> </switch> </g> - <path d="M 251 589 L 251 570 L 270 589 Z" fill="#e1d5e7" stroke="#9673a6" stroke-miterlimit="10" transform="rotate(-180,260.5,579.5)" pointer-events="none"/> - <rect x="0" y="630" width="10" height="10" fill="#ffffff" stroke="none" pointer-events="none"/> - <rect x="1250" y="630" width="10" height="10" fill="#ffffff" stroke="none" pointer-events="none"/> - <rect x="1250" y="0" width="10" height="10" fill="#ffffff" stroke="none" pointer-events="none"/> + <path d="M 251 759 L 251 740 L 270 759 Z" fill="#e1d5e7" stroke="#9673a6" stroke-miterlimit="10" transform="rotate(-180,260.5,749.5)" pointer-events="none"/> + <rect x="0" y="800" width="10" height="10" fill="rgb(255, 255, 255)" stroke="none" pointer-events="none"/> + <rect x="1250" y="0" width="10" height="10" fill="rgb(255, 255, 255)" stroke="none" pointer-events="none"/> + <path d="M 10 520 L 10 520 L 230 520 L 230 520" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 10 520 L 10 630 L 230 630 L 230 520" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> + <text x="119.5" y="537.5"> + Backend App Core + </text> + </g> + <rect x="30" y="550" width="180" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> + <g transform="translate(-0.5 -0.5)"> + <switch> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> + <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 565px; margin-left: 40px;"> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> + @backstage/backend-app-api + </div> + </div> + </div> + </foreignObject> + <text x="40" y="569" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> + @backstage/backend-app-api + </text> + </switch> + </g> + <rect x="30" y="590" width="180" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> + <g transform="translate(-0.5 -0.5)"> + <switch> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> + <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 170px; height: 1px; padding-top: 605px; margin-left: 40px;"> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> + @backstage/backend-defaults + </div> + </div> + </div> + </foreignObject> + <text x="40" y="609" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> + @backstage/backend-defaults + </text> + </switch> + </g> + <path d="M 120 420 Q 120 420 120 513.88" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 120 518.88 L 118.33 513.88 L 121.67 513.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 250 470 L 250 470 L 770 470 L 770 470" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/> + <path d="M 250 470 L 250 640 L 770 640 L 770 470" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/> + <path d="M 259 480 L 259 480 L 490 480 L 490 480" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 259 480 L 259 630 L 490 630 L 490 480" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> + <text x="374" y="497.5"> + Backend Plugin Core + </text> + </g> + <rect x="279" y="510" width="190" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> + <g transform="translate(-0.5 -0.5)"> + <switch> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> + <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 180px; height: 1px; padding-top: 525px; margin-left: 289px;"> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> + @backstage/backend-plugin-api + </div> + </div> + </div> + </foreignObject> + <text x="289" y="529" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> + @backstage/backend-plugin-api + </text> + </switch> + </g> + <rect x="279" y="550" width="190" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> + <g transform="translate(-0.5 -0.5)"> + <switch> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> + <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 180px; height: 1px; padding-top: 565px; margin-left: 289px;"> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> + @backstage/backend-test-utils + </div> + </div> + </div> + </foreignObject> + <text x="289" y="569" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> + @backstage/backend-test-utils + </text> + </switch> + </g> + <rect x="279" y="590" width="190" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> + <g transform="translate(-0.5 -0.5)"> + <switch> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> + <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 180px; height: 1px; padding-top: 605px; margin-left: 289px;"> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> + @backstage/backend-dev-utils + </div> + </div> + </div> + </foreignObject> + <text x="289" y="609" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> + @backstage/backend-dev-utils + </text> + </switch> + </g> + <path d="M 510 480 L 510 480 L 760 480 L 760 480" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 510 480 L 510 630 L 760 630 L 760 480" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <g fill="rgb(0, 0, 0)" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"> + <text x="634.5" y="497.5"> + Backend Libraries + </text> + </g> + <rect x="530" y="510" width="210" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> + <g transform="translate(-0.5 -0.5)"> + <switch> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> + <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 200px; height: 1px; padding-top: 525px; margin-left: 540px;"> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> + @backstage/backend-tasks + </div> + </div> + </div> + </foreignObject> + <text x="540" y="529" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> + @backstage/backend-tasks + </text> + </switch> + </g> + <rect x="525" y="550" width="210" height="30" fill="#fff2cc" stroke="#d6b656" pointer-events="none"/> + <g transform="translate(-0.5 -0.5)"> + <switch> + <foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"> + <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 200px; height: 1px; padding-top: 565px; margin-left: 535px;"> + <div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"> + <div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"> + @backstage/backend-openapi-utils + </div> + </div> + </div> + </foreignObject> + <text x="535" y="569" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"> + @backstage/backend-openapi-utils + </text> + </switch> + </g> + <path d="M 140 420 Q 140 510 243.36 509.95" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> + <path d="M 248.36 509.95 L 243.36 511.62 L 243.36 508.29 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="none"/> </g> <switch> <g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/> <a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"> <text text-anchor="middle" font-size="10px" x="50%" y="100%"> - Viewer does not support full SVG 1.1 + Text is not SVG - cannot display </text> </a> </switch> diff --git a/docs/assets/software-templates/context-menu.png b/docs/assets/software-templates/context-menu.png new file mode 100644 index 0000000000..fa4f52cc48 Binary files /dev/null and b/docs/assets/software-templates/context-menu.png differ diff --git a/docs/assets/software-templates/template-editor-dry-run.png b/docs/assets/software-templates/template-editor-dry-run.png new file mode 100644 index 0000000000..6095fb5cb0 Binary files /dev/null and b/docs/assets/software-templates/template-editor-dry-run.png differ diff --git a/docs/assets/software-templates/template-editor-load-dir.png b/docs/assets/software-templates/template-editor-load-dir.png new file mode 100644 index 0000000000..8fb22ab47c Binary files /dev/null and b/docs/assets/software-templates/template-editor-load-dir.png differ diff --git a/docs/auth/atlassian/provider.md b/docs/auth/atlassian/provider.md index 2680370d93..d6f19ee791 100644 --- a/docs/auth/atlassian/provider.md +++ b/docs/auth/atlassian/provider.md @@ -47,6 +47,12 @@ auth: clientId: ${AUTH_ATLASSIAN_CLIENT_ID} clientSecret: ${AUTH_ATLASSIAN_CLIENT_SECRET} scope: ${AUTH_ATLASSIAN_SCOPES} + signIn: + resolvers: + # typically you would pick one of these + - resolver: emailMatchingUserEntityProfileEmail + - resolver: emailLocalPartMatchingUserEntityName + - resolver: usernameMatchingUserEntityName ``` The Atlassian provider is a structure with three configuration keys: @@ -57,6 +63,18 @@ The Atlassian provider is a structure with three configuration keys: **NOTE:** the scopes `offline_access`, `read:jira-work`, and `read:jira-user` are provided by default. +### Resolvers + +This provider includes several resolvers out of the box that you can use: + +- `emailMatchingUserEntityProfileEmail`: Matches the email address from the auth provider with the User entity that has a matching `spec.profile.email`. If no match is found it will throw a `NotFoundError`. +- `emailLocalPartMatchingUserEntityName`: Matches the [local part](https://en.wikipedia.org/wiki/Email_address#Local-part) of the email address from the auth provider with the User entity that has a matching `name`. If no match is found it will throw a `NotFoundError`. +- `usernameMatchingUserEntityName`: Matches the username from the auth provider with the User entity that has a matching `name`. If no match is found it will throw a `NotFoundError`. + +> Note: The resolvers will be tried in order, but will only be skipped if they throw a `NotFoundError`. + +If these resolvers do not fit your needs you can build a custom resolver, this is covered in the [Building Custom Resolvers](../identity-resolver.md#building-custom-resolvers) section of the Sign-in Identities and Resolvers documentation. + ## Adding the provider to the Backstage frontend To add the provider to the frontend, add the `atlassianAuthApi` reference and diff --git a/docs/auth/github/provider.md b/docs/auth/github/provider.md index 353b8ebe08..2c3aaeaf7d 100644 --- a/docs/auth/github/provider.md +++ b/docs/auth/github/provider.md @@ -41,6 +41,12 @@ auth: clientSecret: ${AUTH_GITHUB_CLIENT_SECRET} ## uncomment if using GitHub Enterprise # enterpriseInstanceUrl: ${AUTH_GITHUB_ENTERPRISE_INSTANCE_URL} + signIn: + resolvers: + # typically you would pick one of these + - resolver: emailMatchingUserEntityProfileEmail + - resolver: emailLocalPartMatchingUserEntityName + - resolver: usernameMatchingUserEntityName ``` The GitHub provider is a structure with three configuration keys: @@ -55,6 +61,18 @@ The GitHub provider is a structure with three configuration keys: `https://your-intermediate-service.com/handler`. Only needed if Backstage is not the immediate receiver (e.g. one OAuth app for many backstage instances). +### Resolvers + +This provider includes several resolvers out of the box that you can use: + +- `emailMatchingUserEntityProfileEmail`: Matches the email address from the auth provider with the User entity that has a matching `spec.profile.email`. If no match is found it will throw a `NotFoundError`. +- `emailLocalPartMatchingUserEntityName`: Matches the [local part](https://en.wikipedia.org/wiki/Email_address#Local-part) of the email address from the auth provider with the User entity that has a matching `name`. If no match is found it will throw a `NotFoundError`. +- `usernameMatchingUserEntityName`: Matches the username from the auth provider with the User entity that has a matching `name`. If no match is found it will throw a `NotFoundError`. + +> Note: The resolvers will be tried in order, but will only be skipped if they throw a `NotFoundError`. + +If these resolvers do not fit your needs you can build a custom resolver, this is covered in the [Building Custom Resolvers](../identity-resolver.md#building-custom-resolvers) section of the Sign-in Identities and Resolvers documentation. + ## Adding the provider to the Backstage frontend To add the provider to the frontend, add the `githubAuthApi` reference and diff --git a/docs/auth/gitlab/provider.md b/docs/auth/gitlab/provider.md index ed7fa6aec3..ff6a341667 100644 --- a/docs/auth/gitlab/provider.md +++ b/docs/auth/gitlab/provider.md @@ -43,6 +43,12 @@ auth: # audience: https://gitlab.company.com ## uncomment if using a custom redirect URI # callbackUrl: https://${BASE_URL}/api/auth/gitlab/handler/frame + signIn: + resolvers: + # typically you would pick one of these + - resolver: emailMatchingUserEntityProfileEmail + - resolver: emailLocalPartMatchingUserEntityName + - resolver: usernameMatchingUserEntityName ``` The GitLab provider is a structure with three configuration keys: @@ -56,6 +62,18 @@ The GitLab provider is a structure with three configuration keys: `https://$backstage.acme.corp/api/auth/gitlab/handler/frame` Note: Due to a peculiarity with GitLab OAuth, ensure there is no trailing `/` after 'frame' in the URL. +### Resolvers + +This provider includes several resolvers out of the box that you can use: + +- `emailMatchingUserEntityProfileEmail`: Matches the email address from the auth provider with the User entity that has a matching `spec.profile.email`. If no match is found it will throw a `NotFoundError`. +- `emailLocalPartMatchingUserEntityName`: Matches the [local part](https://en.wikipedia.org/wiki/Email_address#Local-part) of the email address from the auth provider with the User entity that has a matching `name`. If no match is found it will throw a `NotFoundError`. +- `usernameMatchingUserEntityName`: Matches the username from the auth provider with the User entity that has a matching `name`. If no match is found it will throw a `NotFoundError`. + +> Note: The resolvers will be tried in order, but will only be skipped if they throw a `NotFoundError`. + +If these resolvers do not fit your needs you can build a custom resolver, this is covered in the [Building Custom Resolvers](../identity-resolver.md#building-custom-resolvers) section of the Sign-in Identities and Resolvers documentation. + ## Adding the provider to the Backstage frontend To add the provider to the frontend, add the `gitlabAuthApi` reference and diff --git a/docs/auth/google/gcp-iap-auth.md b/docs/auth/google/gcp-iap-auth.md index 410008b22e..709ef2b521 100644 --- a/docs/auth/google/gcp-iap-auth.md +++ b/docs/auth/google/gcp-iap-auth.md @@ -27,6 +27,12 @@ auth: gcp-iap: audience: '/projects/<project number>/global/backendServices/<backend service id>' jwtHeader: x-custom-header # Optional: Only if you are using a custom header for the IAP JWT + signIn: + resolvers: + # typically you would pick one of these + - resolver: emailMatchingUserEntityProfileEmail + - resolver: emailLocalPartMatchingUserEntityName + - resolver: emailMatchingUserEntityAnnotation ``` The full `audience` value can be obtained by visiting your [Identity-Aware Proxy Google Cloud console](https://console.cloud.google.com/security/iap), selecting your project, finding your Backend Service to proxy, clicking the 3 vertical dots then "Get JWT Audience Code", and copying from the resulting popup, which will look similar to the following: @@ -36,11 +42,44 @@ The full `audience` value can be obtained by visiting your [Identity-Aware Proxy This config section must be in place for the provider to load at all. Now let's add the provider itself. +### Resolvers + +This provider includes several resolvers out of the box that you can use: + +- `emailMatchingUserEntityProfileEmail`: Matches the email address from the auth provider with the User entity that has a matching `spec.profile.email`. If no match is found it will throw a `NotFoundError`. +- `emailLocalPartMatchingUserEntityName`: Matches the [local part](https://en.wikipedia.org/wiki/Email_address#Local-part) of the email address from the auth provider with the User entity that has a matching `name`. If no match is found it will throw a `NotFoundError`. +- `emailMatchingUserEntityAnnotation`: Matches the email address from the auth provider with the User entity where the value of the `google.com/email` annotation matches. If no match is found it will throw a `NotFoundError`. + +> Note: The resolvers will be tried in order, but will only be skipped if they throw a `NotFoundError`. + +If these resolvers do not fit your needs you can build a custom resolver, this is covered in the [Building Custom Resolvers](../identity-resolver.md#building-custom-resolvers) section of the Sign-in Identities and Resolvers documentation. + ## Backend Changes -This provider is not enabled by default in the auth backend code, because -besides the config section above, it also needs to be given one or more -callbacks in actual code as well as described below. +There is a module for this provider that you will need to add to your backend. + +First you'll want to run this command to add the module: + +```sh + yarn --cwd packages/backend add @backstage/plugin-auth-backend-module-gcp-iap-provider +``` + +Then you will need to add this to your backend: + +```ts title="packages/backend/src/index.ts" +const backend = createBackend(); + +backend.add(import('@backstage/plugin-auth-backend')); +/* highlight-add-start */ +backend.add(import('@backstage/plugin-auth-backend-module-gcp-iap-provider')); +/* highlight-add-end */ +``` + +### Legacy Backend Changes + +If you are still using the legacy backend you will need to make the changes outlined here. + +This provider is not enabled by default in the auth backend code, because besides the config section above, it also needs to be given one or more callbacks in actual code as well as described below. Add a `providerFactories` entry to the router in `packages/backend/src/plugins/auth.ts`. diff --git a/docs/auth/google/provider.md b/docs/auth/google/provider.md index d3cd8f2dd2..f2d2e8524e 100644 --- a/docs/auth/google/provider.md +++ b/docs/auth/google/provider.md @@ -42,6 +42,12 @@ auth: development: clientId: ${AUTH_GOOGLE_CLIENT_ID} clientSecret: ${AUTH_GOOGLE_CLIENT_SECRET} + signIn: + resolvers: + # typically you would pick one of these + - resolver: emailMatchingUserEntityProfileEmail + - resolver: emailLocalPartMatchingUserEntityName + - resolver: emailMatchingUserEntityAnnotation ``` The Google provider is a structure with two configuration keys: @@ -50,6 +56,18 @@ The Google provider is a structure with two configuration keys: `10023341500512-beui241gjwwkrdkr2eh7dprewj2pp1q.apps.googleusercontent.com` - `clientSecret`: The client secret tied to the generated client ID. +### Resolvers + +This provider includes several resolvers out of the box that you can use: + +- `emailMatchingUserEntityProfileEmail`: Matches the email address from the auth provider with the User entity that has a matching `spec.profile.email`. If no match is found it will throw a `NotFoundError`. +- `emailLocalPartMatchingUserEntityName`: Matches the [local part](https://en.wikipedia.org/wiki/Email_address#Local-part) of the email address from the auth provider with the User entity that has a matching `name`. If no match is found it will throw a `NotFoundError`. +- `emailMatchingUserEntityAnnotation`: Matches the email address from the auth provider with the User entity where the value of the `google.com/email` annotation matches. If no match is found it will throw a `NotFoundError`. + +> Note: The resolvers will be tried in order, but will only be skipped if they throw a `NotFoundError`. + +If these resolvers do not fit your needs you can build a custom resolver, this is covered in the [Building Custom Resolvers](../identity-resolver.md#building-custom-resolvers) section of the Sign-in Identities and Resolvers documentation. + ## Adding the provider to the Backstage frontend To add the provider to the frontend, add the `googleAuthApi` reference and diff --git a/docs/auth/microsoft/provider.md b/docs/auth/microsoft/provider.md index bb8a7db6e4..5c4707c151 100644 --- a/docs/auth/microsoft/provider.md +++ b/docs/auth/microsoft/provider.md @@ -57,6 +57,12 @@ auth: domainHint: ${AZURE_TENANT_ID} additionalScopes: - Mail.Send + signIn: + resolvers: + # typically you would pick one of these + - resolver: emailMatchingUserEntityProfileEmail + - resolver: emailLocalPartMatchingUserEntityName + - resolver: emailMatchingUserEntityAnnotation ``` The Microsoft provider is a structure with three mandatory configuration keys: @@ -70,6 +76,18 @@ The Microsoft provider is a structure with three mandatory configuration keys: For more details, see [Home Realm Discovery](https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/home-realm-discovery-policy) - `additionalScopes` (optional): List of scopes for the App Registration. The default and mandatory value is ['user.read']. +### Resolvers + +This provider includes several resolvers out of the box that you can use: + +- `emailMatchingUserEntityProfileEmail`: Matches the email address from the auth provider with the User entity that has a matching `spec.profile.email`. If no match is found it will throw a `NotFoundError`. +- `emailLocalPartMatchingUserEntityName`: Matches the [local part](https://en.wikipedia.org/wiki/Email_address#Local-part) of the email address from the auth provider with the User entity that has a matching `name`. If no match is found it will throw a `NotFoundError`. +- `emailMatchingUserEntityAnnotation`: Matches the email address from the auth provider with the User entity where the value of the `microsoft.com/email` annotation matches. If no match is found it will throw a `NotFoundError`. + +> Note: The resolvers will be tried in order, but will only be skipped if they throw a `NotFoundError`. + +If these resolvers do not fit your needs you can build a custom resolver, this is covered in the [Building Custom Resolvers](../identity-resolver.md#building-custom-resolvers) section of the Sign-in Identities and Resolvers documentation. + ## Adding the provider to the Backstage frontend To add the provider to the frontend, add the `microsoftAuthApiRef` reference and diff --git a/docs/auth/oauth2-proxy/provider.md b/docs/auth/oauth2-proxy/provider.md index ef11827039..aded90a1e7 100644 --- a/docs/auth/oauth2-proxy/provider.md +++ b/docs/auth/oauth2-proxy/provider.md @@ -22,44 +22,27 @@ The provider configuration can be added to your `app-config.yaml` under the root auth: environment: development providers: - oauth2Proxy: {} + oauth2Proxy: + development: + signIn: + resolvers: + # typically you would pick one of these + - resolver: emailMatchingUserEntityProfileEmail + - resolver: emailLocalPartMatchingUserEntityName + - resolver: forwardedUserMatchingUserEntityName ``` -Right now no configuration options are supported, but the empty object is needed -to enable the provider in the auth backend. +### Resolvers -To use the `oauth2Proxy` provider you must also configure it with a sign-in resolver. -For more information about the sign-in process in general, see the -[Sign-in Identities and Resolvers](../identity-resolver.md) documentation. +This provider includes several resolvers out of the box that you can use: -For the `oauth2Proxy` provider, the sign-in result is quite different than other providers. -Because it's a proxy provider that can be configured to forward information through -arbitrary headers, the auth result simply just gives you access to the HTTP headers -of the incoming request. Using these you can either extract the information directly, -or grab ID or access tokens to look up additional information and/or validate the request. +- `emailMatchingUserEntityProfileEmail`: Matches the email address from the auth provider with the User entity that has a matching `spec.profile.email`. If no match is found it will throw a `NotFoundError`. +- `emailLocalPartMatchingUserEntityName`: Matches the [local part](https://en.wikipedia.org/wiki/Email_address#Local-part) of the email address from the auth provider with the User entity that has a matching `name`. If no match is found it will throw a `NotFoundError`. +- `forwardedUserMatchingUserEntityName`: Matches the value in the `x-forwarded-user` header from the auth provider with the User entity that has a matching `name`. If no match is found it will throw a `NotFoundError`. -A simple sign-in resolver might for example look like this: +> Note: The resolvers will be tried in order, but will only be skipped if they throw a `NotFoundError`. -```ts -providerFactories: { - ...defaultAuthProviderFactories, - oauth2Proxy: providers.oauth2Proxy.create({ - signIn: { - async resolver({ result }, ctx) { - const name = result.getHeader('x-forwarded-user'); - if (!name) { - throw new Error('Request did not contain a user') - } - return ctx.signInWithCatalogUser({ - entityRef: { name }, - }); - }, - }, - }), -}, -``` - -[An example on how to sign a user in without a matching user](https://github.com/backstage/backstage/blob/master/packages/backend/src/plugins/auth.ts) +If these resolvers do not fit your needs you can build a custom resolver, this is covered in the [Building Custom Resolvers](../identity-resolver.md#building-custom-resolvers) section of the Sign-in Identities and Resolvers documentation. ## Adding the provider to the Backstage frontend diff --git a/docs/auth/okta/provider.md b/docs/auth/okta/provider.md index 2242e4a0f0..0c15746cfd 100644 --- a/docs/auth/okta/provider.md +++ b/docs/auth/okta/provider.md @@ -29,7 +29,7 @@ To add Okta authentication, you must create an Application from Okta: The configuration examples provided above are suitable for local development. For a production deployment, substitute `http://localhost:7007` with the url that your Backstage instance is available at. -# Configuration +## Configuration The provider configuration can then be added to your `app-config.yaml` under the root `auth` configuration: @@ -47,6 +47,12 @@ auth: idp: ${AUTH_OKTA_IDP} # Optional # https://developer.okta.com/docs/reference/api/oidc/#scope-dependent-claims-not-always-returned additionalScopes: ${AUTH_OKTA_ADDITIONAL_SCOPES} # Optional + signIn: + resolvers: + # typically you would pick one of these + - resolver: emailMatchingUserEntityProfileEmail + - resolver: emailLocalPartMatchingUserEntityName + - resolver: emailMatchingUserEntityAnnotation ``` The values referenced are found on the Application page on your Okta site. @@ -61,6 +67,18 @@ The values referenced are found on the Application page on your Okta site. `additionalScopes` is an optional value, a string of space separated scopes, that will be combined with the default `scope` value of `openid profile email offline_access` to adjust the `scope` sent to Okta during OAuth. This will have an impact on [the dependent claims returned](https://developer.okta.com/docs/reference/api/oidc/#scope-dependent-claims-not-always-returned). For example, setting the `additionalScopes` value to `groups` will result in the claim returning a list of the groups that the user is a member of that also match the ID token group filter of the client app. +### Resolvers + +This provider includes several resolvers out of the box that you can use: + +- `emailMatchingUserEntityProfileEmail`: Matches the email address from the auth provider with the User entity that has a matching `spec.profile.email`. If no match is found it will throw a `NotFoundError`. +- `emailLocalPartMatchingUserEntityName`: Matches the [local part](https://en.wikipedia.org/wiki/Email_address#Local-part) of the email address from the auth provider with the User entity that has a matching `name`. If no match is found it will throw a `NotFoundError`. +- `emailMatchingUserEntityAnnotation`: Matches the email address from the auth provider with the User entity where the value of the `okta.com/email` annotation matches. If no match is found it will throw a `NotFoundError`. + +> Note: The resolvers will be tried in order, but will only be skipped if they throw a `NotFoundError`. + +If these resolvers do not fit your needs you can build a custom resolver, this is covered in the [Building Custom Resolvers](../identity-resolver.md#building-custom-resolvers) section of the Sign-in Identities and Resolvers documentation. + ## Adding the provider to the Backstage frontend To add the provider to the frontend, add the `oktaAuthApi` reference and diff --git a/docs/auth/vmware-cloud/provider.md b/docs/auth/vmware-cloud/provider.md index 20115f9e75..8536eaea7e 100644 --- a/docs/auth/vmware-cloud/provider.md +++ b/docs/auth/vmware-cloud/provider.md @@ -107,10 +107,6 @@ export default async function createPlugin( In the above, `commonSignInResolvers.emailLocalPartMatchingUserEntityName()` can be replaced with a more suitable resolver for the app in question. -## Configure Sign-in Resolution - -See [Sign-in Identities and Resolvers](../identity-resolver.md) for details. - ## Add to Sign-in Page See the [Sign-In Configuration](../index.md#sign-in-configuration) docs for @@ -156,9 +152,15 @@ auth: development: clientId: ${APP_ID} organizationId: ${ORG_ID} + signIn: + resolvers: + # typically you would pick one of these + - resolver: emailMatchingUserEntityProfileEmail + - resolver: emailLocalPartMatchingUserEntityName + - resolver: vmwareCloudSignInResolvers ``` -where `APP_ID` refers to the ID retrieved when creating the OAuth App, and +Where `APP_ID` refers to the ID retrieved when creating the OAuth App, and `ORG_ID` is the [long ID of the Organization](https://docs.vmware.com/en/VMware-Cloud-services/services/Using-VMware-Cloud-Services/GUID-CF9E9318-B811-48CF-8499-9419997DC1F8.html#view-the-organization-id-1) in VMware Cloud for which you wish to enable sign-in. @@ -169,3 +171,15 @@ library used by this provider requires the use of Express session middleware to do this. Therefore the value `your session secret` under `auth.session.secret` should be replaced with a long, complex and unique string which will act as a key for signing session cookies set by Backstage. + +### Resolvers + +This provider includes several resolvers out of the box that you can use: + +- `emailMatchingUserEntityProfileEmail`: Matches the email address from the auth provider with the User entity that has a matching `spec.profile.email`. If no match is found it will throw a `NotFoundError`. +- `emailLocalPartMatchingUserEntityName`: Matches the [local part](https://en.wikipedia.org/wiki/Email_address#Local-part) of the email address from the auth provider with the User entity that has a matching `name`. If no match is found it will throw a `NotFoundError`. +- `vmwareCloudSignInResolvers`: Matches the email address from the auth provider with the User entity that has a matching `spec.profile.email`. If no match is found it will sign in the user without associating with a catalog user. + +> Note: The resolvers will be tried in order, but will only be skipped if they throw a `NotFoundError`. + +If these resolvers do not fit your needs you can build a custom resolver, this is covered in the [Building Custom Resolvers](../identity-resolver.md#building-custom-resolvers) section of the Sign-in Identities and Resolvers documentation. diff --git a/docs/backend-system/building-backends/08-migrating.md b/docs/backend-system/building-backends/08-migrating.md index 1d7e1dcd03..0fbb368449 100644 --- a/docs/backend-system/building-backends/08-migrating.md +++ b/docs/backend-system/building-backends/08-migrating.md @@ -816,9 +816,9 @@ auth: tenantId: ${AZURE_TENANT_ID} signIn: resolvers: - - resolver: emailMatchingUserEntityAnnotation - resolver: emailMatchingUserEntityProfileEmail - resolver: emailLocalPartMatchingUserEntityName + - resolver: emailMatchingUserEntityAnnotation ``` > Note: the resolvers will be tried in order, but will only be skipped if they throw a `NotFoundError`. @@ -848,7 +848,7 @@ Additional resolvers: - [usernameMatchingUserEntityName](https://github.com/backstage/backstage/blob/5447cffd23cf00772988fb799ced0ec5e54efb2e/plugins/auth-backend-module-atlassian-provider/src/resolvers.ts#L33C16-L33C46) -##### GCP IAM +##### GCP IAP (Google Identity-Aware Proxy) Setup: diff --git a/docs/contribute/getting-involved.md b/docs/contribute/getting-involved.md index 7c0be46273..c11e870752 100644 --- a/docs/contribute/getting-involved.md +++ b/docs/contribute/getting-involved.md @@ -17,15 +17,15 @@ Look through the GitHub issues for [bugs](https://github.com/backstage/backstage ### Build a plugin -The value of Backstage grows with every new plugin that gets added. Wouldn't it be fantastic if there was a plugin for every infrastructure project out there? We think so. And we would love your help. +The value of Backstage grows with every new plugin that gets added. Wouldn't it be fantastic if there was a plugin for every infrastructure project out there? We think so. And we would love your help. Head over to [the community plugins repo](https://github.com/backstage/community-plugins) and get started! A great reference example of a plugin can be found on [our blog](https://backstage.io/blog/2020/04/06/lighthouse-plugin) (thanks [@fastfrwrd](https://github.com/fastfrwrd)!) -What kind of plugins should/could be created? Some inspiration from the 120+ plugins that we have developed inside Spotify can be found [here](https://backstage.io/demos), but we will keep a running list of suggestions labeled with [[plugin]](https://github.com/backstage/backstage/labels/plugin). +What kind of plugins should/could be created? Some inspiration from the 120+ plugins that we have developed inside Spotify can be found [here](https://backstage.io/demos), but we will keep a running list of suggestions labeled with [[plugin]](https://github.com/backstage/community-plugins/labels/plugin) in our [community plugins repo](https://github.com/backstage/community-plugins). ### Suggesting a plugin -If you start developing a plugin that you aim to release as open source, we suggest that you create a [new Issue](https://github.com/backstage/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). This helps the community know what plugins are in development. +If you start developing a plugin that you aim to release as open source, we suggest that you create a [new Issue](https://github.com/backstage/community-plugins/issues/new/choose) in the community plugins repo. This helps the community know what plugins are in development. You can also use this process if you have an idea for a good plugin but you hope that someone else will pick up the work. diff --git a/docs/deployment/docker.md b/docs/deployment/docker.md index 791882224d..9ec5460a88 100644 --- a/docs/deployment/docker.md +++ b/docs/deployment/docker.md @@ -345,8 +345,8 @@ The `Dockerfile` mentioned above located in `packages/backend` is maintained by ### Minimal Hardened Image -A contributed `Dockerfile` exists within the directory of `contrib/docker/minimal-harded-image` which uses the [`wolfi-base`](https://github.com/wolfi-dev) image to reduce vulnerabilities. When this was contributed, this alternative `Dockerfile` reduced 98.2% of vulnerabilities in the built Backstage docker image when compared with the image built from `packages/backend/Dockerfile`. +A contributed `Dockerfile` exists within the directory of `contrib/docker/minimal-hardened-image` which uses the [`wolfi-base`](https://github.com/wolfi-dev) image to reduce vulnerabilities. When this was contributed, this alternative `Dockerfile` reduced 98.2% of vulnerabilities in the built Backstage docker image when compared with the image built from `packages/backend/Dockerfile`. -To reduce maintenance, the digest of the image has been removed from the `contrib/docker/minimal-harded-image/Dockerfile` file. A complete example with the digest would be `cgr.dev/chainguard/wolfi-base:latest@sha256:3d6dece13cdb5546cd03b20e14f9af354bc1a56ab5a7b47dca3e6c1557211fcf` and it is suggested to update the `FROM` line in the `Dockerfile` to use a digest. Please do a docker pull on the image to get the latest digest. Using the digest allows tools such as Dependabot or Renovate to know exactly which image digest is being utilized and allows for Pull Requests to be triggered when a new digest is available. +To reduce maintenance, the digest of the image has been removed from the `contrib/docker/minimal-hardened-image/Dockerfile` file. A complete example with the digest would be `cgr.dev/chainguard/wolfi-base:latest@sha256:3d6dece13cdb5546cd03b20e14f9af354bc1a56ab5a7b47dca3e6c1557211fcf` and it is suggested to update the `FROM` line in the `Dockerfile` to use a digest. Please do a docker pull on the image to get the latest digest. Using the digest allows tools such as Dependabot or Renovate to know exactly which image digest is being utilized and allows for Pull Requests to be triggered when a new digest is available. It is suggested to setup Dependabot/Renovate or a similar tool to ensure the image is kept up to date so that vulnerability fixes that have been addressed are pulled in frequently. diff --git a/docs/faq/product.md b/docs/faq/product.md index 79465cf53d..edbb1ffb4d 100644 --- a/docs/faq/product.md +++ b/docs/faq/product.md @@ -37,7 +37,7 @@ more, read our blog post, Yes, we've already started releasing open source versions of some of the plugins we use here, and we'll continue to do so. -[Plugins](#what-is-a-plugin-in-backstage) are the building blocks of +[Plugins](technical.md#what-is-a-plugin-in-backstage) are the building blocks of functionality in Backstage. We have over 120 plugins inside Spotify — many of those are specialized for our use, so will remain internal and proprietary to us. But we estimate that about a third of our existing plugins make good open diff --git a/docs/faq/technical.md b/docs/faq/technical.md index 630e516a35..d64d81d499 100644 --- a/docs/faq/technical.md +++ b/docs/faq/technical.md @@ -100,10 +100,10 @@ You can browse and search for all available plugins in the [Plugin Directory](https://backstage.io/plugins). If you can't find it in the directory, before you write a plugin -[search the plugin issues](https://github.com/backstage/backstage/issues?q=is%3Aissue+label%3Aplugin+) +[search the plugin issues on the community plugins repo](https://github.com/backstage/community-plugins/issues?q=is%3Aissue+is%3Aopen+label%3Aplugin) to see if is in the works. If no one's thought of it yet, great! Open a new issue as -[a plugin suggestion](https://github.com/backstage/backstage/issues/new/choose) +[a plugin suggestion](https://github.com/backstage/community-plugins/issues/new/choose) and describe what your plugin will do. This will help coordinate our contributors' efforts and avoid duplicating existing functionality. @@ -154,7 +154,7 @@ maintains Backstage in your own environment. For more information, see our [Owners](https://github.com/backstage/backstage/blob/master/OWNERS.md) and -[Governance](https://github.com/backstage/backstage/blob/master/GOVERNANCE.md). +[Governance](https://github.com/backstage/community/blob/main/GOVERNANCE.md). ### Does Spotify provide a managed version of Backstage? diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index f64925068a..d1fac931e5 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -851,3 +851,39 @@ export default async function createPlugin({ } }); ``` + +## Template Editor + +Writing template is most of the times an iterative process. You will need to test your template to make sure it has a good user experience and that it works as expected. To help on this process the scaffolder comes with a build in template editor that allows you to test your template in a real environment for querying data and execute the actions on dry-run mode to see the results of those one. + +To access to the template editor you can go to the templates page and select "Template Editor" from the context menu or navigate to the `{scaffolder-path}/edit` url. (i.e. the default route would be `/create/edit`) + +![Context menu](../../assets/software-templates/context-menu.png) + +The template editor has 3 main sections: + +1. **Load Template Directory**: Load a local template directory, allowing you to both edit and try executing your own template. +2. **Edit Template Form**: Preview and edit a template form, either using a sample template or by loading a template from the catalog. +3. **Custom Field Explorer**: View and play around with available installed custom field extensions. + +### Load Template Directory + +Allow to load a directory on your local file system that contains a template and editing the files in it while previewing the form and executing the template. + +![template editor load dir](../../assets/software-templates/template-editor-load-dir.png) + +If you complete the form in the right side and click on `Create` button, the template will be executed in dry-run mode and the result will be shown in the `Dry-run result` drawer that will pop-up at the bottom of the screen. + +Here we could find all the file system results of the template execution as well as the logs of each action that was executed. + +![dry run drawer example](../../assets/software-templates/template-editor-dry-run.png) + +### Edit Template Form + +This is a reduced version of the template editor that allows you to select any template from the catalog and do some modifications on the form presented to the user to test some changes. + +Have in mind that changes in this form will not be saved on the template and is meant to test out changes to replicate them manually on the template file after. + +### Custom Field Explorer + +The custom filed explorer allows you to select any custom field loaded on the backstage instance and test different values and configurations. diff --git a/docs/frontend-system/building-apps/02-configuring-extensions.md b/docs/frontend-system/building-apps/02-configuring-extensions.md index 2fd4ce69fc..4052ef47f0 100644 --- a/docs/frontend-system/building-apps/02-configuring-extensions.md +++ b/docs/frontend-system/building-apps/02-configuring-extensions.md @@ -6,4 +6,50 @@ sidebar_label: Configuring Extensions description: Documentation for how to configure extensions in a Backstage app --- -TODO +All extensions in a Backstage app can be configured through static configuration. This configuration is all done under the `app.extensions` configuration key. For more general information on how to write configuration for Backstage, see the section on [writing configuration](../../conf/writing.md). + +## Extension Configuration Schema + +This section focuses on the format of the `app.extensions` configuration and the various shorthands that are available. + +The most complete and verbose format for configuring an individual extensions is as follows: + +```yaml +app: + extensions: + - <id>: + attachTo: + id: <parent-id> + input: <input-name> + disabled: <true/false> + config: <extension-specific-config-> +``` + +All of the top-level fields are optional: `attachTo`, `disabled`, and `config`. Every extension implementation must provide defaults for all of these fields that will be used if they are not provided in the configuration. + +Note that `app.extensions` is always an array rather than an object. For example, the following is invalid: + +```yaml title="INVALID" +app: + extensions: + <id>: # Invalid, this should be an array item, `app.extensions` is now an object + config: ... +``` + +In addition to this schema, there are a number of shorthands available: + +Rather than a full object, you can specify just the ID of the extension as a string. This is equivalent to setting `disabled` to `false`: + +```yaml +app: + extensions: + - ‘<id>’ +``` + +You can enable/disable individual extension by ID, in this case the value is a boolean: + +```yaml +app: + extensions: + - <id>: <true/false> +``` diff --git a/docs/frontend-system/building-apps/08-migrating.md b/docs/frontend-system/building-apps/08-migrating.md index 937e865133..0abdd2aac3 100644 --- a/docs/frontend-system/building-apps/08-migrating.md +++ b/docs/frontend-system/building-apps/08-migrating.md @@ -117,7 +117,7 @@ You can then also add any additional extensions that you may need to create as p [Utility API](../utility-apis/01-index.md) factories are now installed as extensions instead. Pass the existing factory to `createApiExtension` and install it in the app. For more information, see the section on [configuring Utility APIs](../utility-apis/04-configuring.md). -For example, the following API configuration: +For example, the following `apis` configuration: ```ts const app = createApp({ @@ -151,15 +151,75 @@ Icons are currently installed through the usual options to `createApp`, but will Plugins are now passed through the `features` options instead. +For example, the following `plugins` configuration: + +```tsx +import { homePlugin } from '@backstage/plugin-home'; + +createApp({ + // ... + plugins: [homePlugin], + // ... +}); +``` + +Can be converted to the following `features` configuration: + +```tsx +// plugins are now default exported via alpha subpath +import homePlugin from '@backstage/plugin-home/alpha'; + +createApp({ + // ... + features: [homePlugin], + // ... +}); +``` + +Plugins don't even have to be imported manually after installing their package if [features discovery](../architecture/02-app.md#feature-discovery) is enabled. + +```yaml title="in app-config.yaml" +app: + # Enabling plugin and override features discovery + experimental: 'all' +``` + ### `featureFlags` Declaring features flags in the app is no longer supported, move these declarations to the appropriate plugins instead. +For example, the following app feature flags configuration: + +```tsx +createApp({ + // ... + featureFlags: [ + { + pluginId: '', + name: 'tech-radar', + description: 'Enables the tech radar plugin', + }, + ], + // ... +}); +``` + +Can be converted to the following plugin configuration: + +```tsx +createPlugin({ + id: 'tech-radar', + // ... + featureFlags: [{ name: 'tech-radar' }], + // ... +}); +``` + ### `components` Many app components are now installed as extensions instead using `createComponentExtension`. See the section on [configuring app components](./01-index.md#configure-your-app) for more information. -The `Router` component is now a built-in extension that you can override using `createRouterExtension`. +The `Router` component is now a built-in extension that you can [override](../architecture/05-extension-overrides.md) using `createRouterExtension`. The Sign-in page is now installed as an extension using the `createSignInPageExtension` instead. @@ -277,6 +337,35 @@ const app = createApp({ Translations are now installed as extensions, using `createTranslationExtension`. +For example, the following translations configuration: + +```tsx +import { catalogTranslationRef } from '@backstage/plugin-catalog/alpha'; +createApp({ + // ... + __experimentalTranslations: { + resources: [ + createTranslationMessages({ + ref: catalogTranslationRef, + catalog_page_create_button_title: 'Create Software', + }), + ], + }, + // ... +}); +``` + +Can be converted to the following extension: + +```tsx +createTranslationExtension({ + resource: createTranslationMessages({ + ref: catalogTranslationRef, + catalog_page_create_button_title: 'Create Software', + }), +}); +``` + ## Gradual Migration After updating all `createApp` options as well as using `convertLegacyApp` to use your existing app structure, you should be able to start up the app and see that it still works. If that is not the case, make sure you read any error messages that you may see in the app as they can provide hints on what you need to fix. If you are still stuck, you can check if anyone else ran into the same issue in our [GitHub issues](https://github.com/backstage/backstage/issues), or ask for help in our [community Discord](https://discord.gg/backstage-687207715902193673). @@ -368,7 +457,7 @@ The entity pages are typically defined in `packages/app/src/components/catalog` New apps feature a built-in sidebar extension (`app/nav`) that will render all nav item extensions provided by plugins. This is a placeholder implementation and not intended as a long-term solution. In the future we will aim to provide a more flexible sidebar extension that allows for more customization out of the box. -Because the built-in sidebar is quite limited you may want to override the sidebar with your own custom implementation. To do so, use `createExtension` directly and refer to the [original sidebar implementation](https://github.com/backstage/backstage/blob/master/packages/frontend-app-api/src/extensions/AppNav.tsx). The following is an example of how to take your existing sidebar from the `Root` component that you typically find in `packages/app/src/components/Root.tsx`, and use it in an extension override: +Because the built-in sidebar is quite limited you may want to override the sidebar with your own custom implementation. To do so, use `createExtension` directly and refer to the [original sidebar implementation](https://github.com/backstage/backstage/blob/master/packages/frontend-app-api/src/extensions/AppNav.tsx). The following is an example of how to take your existing sidebar from the `Root` component that you typically find in `packages/app/src/components/Root.tsx`, and use it in an [extension override](../architecture/05-extension-overrides.md): ```tsx const nav = createExtension({ @@ -435,3 +524,24 @@ export default app.createRoot( ``` Any app root wrapper needs to be migrated to be an extension, using `createAppRootWrapperExtension`. Note that if you have multiple wrappers they must be completely independent of each other, i.e. the order in which they the appear in the React tree should not matter. If that is not the case then you should group them into a single wrapper. + +Here is an example converting the `CustomAppBarrier` into extension: + +```tsx +createApp({ + // ... + features: [ + createExtensionOverrides({ + extensions: [ + createAppRootWrapperExtension({ + name: 'CustomAppBarrier', + // Whenever your component uses legacy core packages, wrap it with "compatWrapper" + // e.g. props => compatWrapper(<CustomAppBarrier {...props} />) + Component: CustomAppBarrier, + }), + ], + }), + ], + // ... +}); +``` diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index 1690035b9b..99fdb3d8ed 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -164,7 +164,7 @@ Share your experiences, comments, or suggestions with us: [on discord](https://discord.gg/backstage-687207715902193673), file issues for any [feature](https://github.com/backstage/backstage/issues/new?labels=help+wanted&template=feature_template.md) or -[plugin suggestions](https://github.com/backstage/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME), +[plugin suggestions](https://github.com/backstage/community-plugins/issues/new/choose), or [bugs](https://github.com/backstage/backstage/issues/new?labels=bug&template=bug_template.md) you have, and feel free to diff --git a/docs/integrations/azure/discovery--old.md b/docs/integrations/azure/discovery--old.md new file mode 100644 index 0000000000..be490d70fe --- /dev/null +++ b/docs/integrations/azure/discovery--old.md @@ -0,0 +1,187 @@ +--- +id: discovery--old +title: Azure DevOps Discovery +sidebar_label: Discovery +# prettier-ignore +description: Automatically discovering catalog entities from repositories in an Azure DevOps organization +--- + +:::info +This documentation is written for the old backend which has been replaced by [the new backend system](../../backend-system/index.md), being the default since Backstage [version 1.24](../../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./discovery.md) instead. Otherwise, [consider migrating](../../backend-system/building-backends/08-migrating.md)! +::: + +The Azure DevOps integration has a special entity provider for discovering +catalog entities within an Azure DevOps. The provider will crawl your Azure +DevOps organization and register entities matching the configured path. This can +be useful as an alternative to static locations or manually adding things to the +catalog. + +This guide explains how to install and configure the Azure DevOps Entity Provider (recommended) or the Azure DevOps Processor. + +## Dependencies + +### Code Search Feature + +Azure discovery is driven by the Code Search feature in Azure DevOps, this may not be enabled by default. For Azure +DevOps Services you can confirm this by looking at the installed extensions in your Organization Settings. For Azure +DevOps Server you'll find this information in your Collection Settings. + +If the Code Search extension is not listed then you can install it from the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=ms.vss-code-search&targetId=f9352dac-ba6e-434e-9241-a848a510ce3f&utm_source=vstsproduct&utm_medium=SearchExtStatus). + +### Azure Integration + +Setup [Azure integration](locations.md) with `host` and `token`. Host must be `dev.azure.com` for Cloud users, otherwise set this to your on-premise hostname. + +## Installation + +At your configuration, you add one or more provider configs: + +```yaml title="app-config.yaml" +catalog: + providers: + azureDevOps: + yourProviderId: # identifies your dataset / provider independent of config changes + organization: myorg + project: myproject + repository: service-* # this will match all repos starting with service-* + path: /catalog-info.yaml + schedule: # optional; same options as in TaskScheduleDefinition + # supports cron, ISO duration, "human duration" as used in code + frequency: { minutes: 30 } + # supports ISO duration, "human duration" as used in code + timeout: { minutes: 3 } + yourSecondProviderId: # identifies your dataset / provider independent of config changes + organization: myorg + project: '*' # this will match all projects + repository: '*' # this will match all repos + path: /catalog-info.yaml + anotherProviderId: # another identifier + organization: myorg + project: myproject + repository: '*' # this will match all repos + path: /src/*/catalog-info.yaml # this will search for files deep inside the /src folder + yetAnotherProviderId: # guess, what? Another one :) + host: selfhostedazure.yourcompany.com + organization: myorg + project: myproject + branch: development +``` + +The parameters available are: + +- **`host:`** _(optional)_ Leave empty for Cloud hosted, otherwise set to your self-hosted instance host. +- **`organization:`** Your Organization slug (or Collection for on-premise users). Required. +- **`project:`** _(required)_ Your project slug. Wildcards are supported as shown on the examples above. Using '\*' will search all projects. For a project name containing spaces, use both single and double quotes as in `project: '"My Project Name"'`. +- **`repository:`** _(optional)_ The repository name. Wildcards are supported as show on the examples above. If not set, all repositories will be searched. +- **`path:`** _(optional)_ Where to find catalog-info.yaml files. Defaults to /catalog-info.yaml. +- **`branch:`** _(optional)_ The branch name to use. +- **`schedule`**: + - **`frequency`**: + How often you want the task to run. The system does its best to avoid overlapping invocations. + - **`timeout`**: + The maximum amount of time that a single task invocation can take. + - **`initialDelay`** _(optional)_: + The amount of time that should pass before the first invocation happens. + - **`scope`** _(optional)_: + `'global'` or `'local'`. Sets the scope of concurrency control. + +_Note:_ + +- The path parameter follows the same rules as the search on Azure DevOps web interface. For more details visit the [official search documentation](https://docs.microsoft.com/en-us/azure/devops/project/search/get-started-search?view=azure-devops). +- To use branch parameters, it is necessary that the desired branch be added to the "Searchable branches" list within Azure DevOps Repositories. To do this, follow the instructions below: + +1. Access your Azure DevOps and open the repository in which you want to add the branch. +2. Click on "Settings" in the lower-left corner of the screen. +3. Select the "Options" option in the left navigation bar. +4. In the "Searchable branches" section, click on the "Add" button to add a new branch. +5. In the window that appears, enter the name of the branch you want to add and click "Add". +6. The added branch will now appear in the "Searchable branches" list. + +It may take some time before the branch is indexed and searchable. + +As this provider is not one of the default providers, you will first need to install +the Azure catalog plugin: + +```bash +# From your Backstage root directory +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-azure +``` + +Once you've done that, you'll also need to add the segment below to `packages/backend/src/plugins/catalog.ts`: + +```ts title="packages/backend/src/plugins/catalog.ts" +/* highlight-add-next-line */ +import { AzureDevOpsEntityProvider } from '@backstage/plugin-catalog-backend-module-azure'; + +const builder = await CatalogBuilder.create(env); +/** ... other processors and/or providers ... */ +/* highlight-add-start */ +builder.addEntityProvider( + AzureDevOpsEntityProvider.fromConfig(env.config, { + logger: env.logger, + // optional: alternatively, use scheduler with schedule defined in app-config.yaml + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), + // optional: alternatively, use schedule + scheduler: env.scheduler, + }), +); +/* highlight-add-end */ +``` + +## Alternative Processor + +As an alternative to the entity provider `AzureDevOpsEntityProvider`, you can still use the `AzureDevopsDiscoveryProcessor`. + +```ts title="packages/backend/src/plugins/catalog.ts" +/* highlight-add-next-line */ +import { AzureDevOpsDiscoveryProcessor } from '@backstage/plugin-catalog-backend-module-azure'; + +export default async function createPlugin( + env: PluginEnvironment, +): Promise<Router> { + const builder = await CatalogBuilder.create(env); + /* highlight-add-next-line */ + builder.addProcessor( + AzureDevOpsDiscoveryProcessor.fromConfig(env.config, { + logger: env.logger, + }), + ); + + // .. +} +``` + +```yaml +catalog: + locations: + # Scan all repositories for a catalog-info.yaml in the root of the default branch + - type: azure-discovery + target: https://dev.azure.com/myorg/myproject + # Or use a custom pattern for a subset of all repositories with default repository + - type: azure-discovery + target: https://dev.azure.com/myorg/myproject/_git/service-* + # Or use a custom file format and location + - type: azure-discovery + target: https://dev.azure.com/myorg/myproject/_git/*?path=/src/*/catalog-info.yaml + # And optionally provide a specific branch name using the version parameter + - type: azure-discovery + target: https://dev.azure.com/myorg/myproject/_git/*?path=/catalog-info.yaml&version=GBtopic/catalog-info +``` + +Note the `azure-discovery` type, as this is not a regular `url` processor. + +When using a custom pattern, the target is composed of these parts: + +- The base instance URL, `https://dev.azure.com` in this case +- The organization name which is required, `myorg` in this case +- The project name which is optional, `myproject` in this case. This defaults to \*, which scans all the projects where the token has access to. +- The repository blob to scan, which accepts \* wildcard tokens and must be + added after `_git/`. This can simply be `*` to scan all repositories in the + project. +- The path within each repository to find the catalog YAML file. This will + usually be `/catalog-info.yaml`, `/src/*/catalog-info.yaml` or a similar + variation for catalog files stored in the root directory of each repository. +- The repository branch to scan which is optional, `topic/catalog-info` in this case. If omitted, the repo's default branch will be scanned. The `GB` prefix is required, as this is how Azure DevOps identifies the version as a branch. diff --git a/docs/integrations/azure/discovery.md b/docs/integrations/azure/discovery.md index 7fbba5228d..7e7b465118 100644 --- a/docs/integrations/azure/discovery.md +++ b/docs/integrations/azure/discovery.md @@ -6,6 +6,10 @@ sidebar_label: Discovery description: Automatically discovering catalog entities from repositories in an Azure DevOps organization --- +:::info +This documentation is written for [the new backend system](../../backend-system/index.md) which is the default since Backstage [version 1.24](../../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./discovery--old.md) instead, and [consider migrating](../../backend-system/building-backends/08-migrating.md)! +::: + The Azure DevOps integration has a special entity provider for discovering catalog entities within an Azure DevOps. The provider will crawl your Azure DevOps organization and register entities matching the configured path. This can @@ -30,7 +34,7 @@ Setup [Azure integration](locations.md) with `host` and `token`. Host must be `d ## Installation -At your configuration, you add one or more provider configs: +In your configuration, you add one or more provider configs: ```yaml title="app-config.yaml" catalog: @@ -103,81 +107,11 @@ the Azure catalog plugin: yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-azure ``` -Once you've done that, you'll also need to add the segment below to `packages/backend/src/plugins/catalog.ts`: +Then updated your backend by adding the following line: -```ts title="packages/backend/src/plugins/catalog.ts" -/* highlight-add-next-line */ -import { AzureDevOpsEntityProvider } from '@backstage/plugin-catalog-backend-module-azure'; - -const builder = await CatalogBuilder.create(env); -/** ... other processors and/or providers ... */ +```ts title="packages/backend/src/index.ts" +backend.add(import('@backstage/plugin-catalog-backend/alpha')); /* highlight-add-start */ -builder.addEntityProvider( - AzureDevOpsEntityProvider.fromConfig(env.config, { - logger: env.logger, - // optional: alternatively, use scheduler with schedule defined in app-config.yaml - schedule: env.scheduler.createScheduledTaskRunner({ - frequency: { minutes: 30 }, - timeout: { minutes: 3 }, - }), - // optional: alternatively, use schedule - scheduler: env.scheduler, - }), -); +backend.add(import('@backstage/plugin-catalog-backend-module-azure/alpha')); /* highlight-add-end */ ``` - -## Alternative Processor - -As an alternative to the entity provider `AzureDevOpsEntityProvider`, you can still use the `AzureDevopsDiscoveryProcessor`. - -```ts title="packages/backend/src/plugins/catalog.ts" -/* highlight-add-next-line */ -import { AzureDevOpsDiscoveryProcessor } from '@backstage/plugin-catalog-backend-module-azure'; - -export default async function createPlugin( - env: PluginEnvironment, -): Promise<Router> { - const builder = await CatalogBuilder.create(env); - /* highlight-add-next-line */ - builder.addProcessor( - AzureDevOpsDiscoveryProcessor.fromConfig(env.config, { - logger: env.logger, - }), - ); - - // .. -} -``` - -```yaml -catalog: - locations: - # Scan all repositories for a catalog-info.yaml in the root of the default branch - - type: azure-discovery - target: https://dev.azure.com/myorg/myproject - # Or use a custom pattern for a subset of all repositories with default repository - - type: azure-discovery - target: https://dev.azure.com/myorg/myproject/_git/service-* - # Or use a custom file format and location - - type: azure-discovery - target: https://dev.azure.com/myorg/myproject/_git/*?path=/src/*/catalog-info.yaml - # And optionally provide a specific branch name using the version parameter - - type: azure-discovery - target: https://dev.azure.com/myorg/myproject/_git/*?path=/catalog-info.yaml&version=GBtopic/catalog-info -``` - -Note the `azure-discovery` type, as this is not a regular `url` processor. - -When using a custom pattern, the target is composed of these parts: - -- The base instance URL, `https://dev.azure.com` in this case -- The organization name which is required, `myorg` in this case -- The project name which is optional, `myproject` in this case. This defaults to \*, which scans all the projects where the token has access to. -- The repository blob to scan, which accepts \* wildcard tokens and must be - added after `_git/`. This can simply be `*` to scan all repositories in the - project. -- The path within each repository to find the catalog YAML file. This will - usually be `/catalog-info.yaml`, `/src/*/catalog-info.yaml` or a similar - variation for catalog files stored in the root directory of each repository. -- The repository branch to scan which is optional, `topic/catalog-info` in this case. If omitted, the repo's default branch will be scanned. The `GB` prefix is required, as this is how Azure DevOps identifies the version as a branch. diff --git a/docs/integrations/azure/org--old.md b/docs/integrations/azure/org--old.md new file mode 100644 index 0000000000..13d53b24b3 --- /dev/null +++ b/docs/integrations/azure/org--old.md @@ -0,0 +1,274 @@ +--- +id: org--old +title: Microsoft Entra Tenant Data +sidebar_label: Org Data +# prettier-ignore +description: Importing users and groups from Microsoft Entra ID into Backstage +--- + +:::info +This documentation is written for the old backend which has been replaced by [the new backend system](../../backend-system/index.md), being the default since Backstage [version 1.24](../../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./org.md) instead.Otherwise, [consider migrating](../../backend-system/building-backends/08-migrating.md)! +::: + +The Backstage catalog can be set up to ingest organizational data - users and +teams - directly from a tenant in Microsoft Entra ID via the +Microsoft Graph API. + +## Installation + +The package is not installed by default, therefore you have to add `@backstage/plugin-catalog-backend-module-msgraph` to your backend package. + +```bash +# From your Backstage root directory +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-msgraph +``` + +Next add the basic configuration to `app-config.yaml` + +```yaml title="app-config.yaml" +catalog: + providers: + microsoftGraphOrg: + default: + tenantId: ${AZURE_TENANT_ID} + user: + filter: accountEnabled eq true and userType eq 'member' + group: + filter: > + securityEnabled eq false + and mailEnabled eq true + and groupTypes/any(c:c+eq+'Unified') + schedule: + frequency: PT1H + timeout: PT50M +``` + +Finally, register the plugin in `catalog.ts`. +For large organizations, this plugin can take a long time, so be careful setting low frequency / timeouts and importing a large amount of users / groups for the first try. + +```ts title="packages/backend/src/plugins/catalog.ts" +/* highlight-add-next-line */ +import { MicrosoftGraphOrgEntityProvider } from '@backstage/plugin-catalog-backend-module-msgraph'; + +export default async function createPlugin( + env: PluginEnvironment, +): Promise<Router> { + const builder = await CatalogBuilder.create(env); + + /* highlight-add-start */ + builder.addEntityProvider( + MicrosoftGraphOrgEntityProvider.fromConfig(env.config, { + logger: env.logger, + scheduler: env.scheduler, + }), + ); + /* highlight-add-end */ + + // .. +} +``` + +## Authenticating with Microsoft Graph + +### Local Development + +For a local dev environment, it's recommended you have the Azure CLI or Azure PowerShell installed, and are logged in to those. +Alternatively you can use VSCode with the Azure extension if you install `@azure/identity-vscode`. +When these are set up, the plugin will authenticate with the Microsoft Graph API without you needing to configure any credentials, or granting any special permissions. +If you can't do this, you'll have to create an App Registration. + +### App Registration + +If none of the other authentication methods work, you can create an app registration in the azure portal. +By default the graph plugin requires the following Application permissions (not Delegated) for Microsoft Graph: + +- `GroupMember.Read.All` +- `User.Read.All` + +If your organization required Admin Consent for these permissions, that will need to be granted. + +When authenticating with a ClientId/ClientSecret, you can either set the `AZURE_TENANT_ID`, `AZURE_CLIENT_ID` and `AZURE_CLIENT_SECRET` environment variables, or specify the values in configuration + +```yaml +microsoftGraphOrg: + default: + ##... + clientId: 9ef1aac6-b454-4e69-9cf5-7199df049281 + clientSecret: REDACTED +``` + +To authenticate with a certificate rather than a client secret, you can set the `AZURE_TENANT_ID`, `AZURE_CLIENT_ID` and `AZURE_CLIENT_CERTIFICATE_PATH` environments + +### Managed Identity + +If deploying to resources that supports Managed Identity, and has identities configured (e.g. Azure App Services, Azure Container Apps), Managed Identity should be picked up without any additional configuration. +If your app has multiple managed identities, you may need to set the `AZURE_CLIENT_ID` environment variable to tell Azure Identity which identity to use. + +To grant the managed identity the same permissions as mentioned in _App Registration_ above, [please follow this guide](https://docs.microsoft.com/en-us/azure/app-service/tutorial-connect-app-access-microsoft-graph-as-app-javascript?tabs=azure-powershell) + +## Filtering imported Users and Groups + +By default, the plugin will import all users and groups from your directory. +This can be customized through [filters](https://learn.microsoft.com/en-us/graph/filter-query-parameter) and [search](https://learn.microsoft.com/en-us/graph/search-query-parameter) queries. Keep in mind that if you omit filters and search queries for the user or group properties, the plugin will automatically import all available users or groups. + +### Groups + +A smaller set of groups can be obtained by configuring a search query or a filter. +If both `filter` and `search` are provided, then groups must match both to be ingested. + +```yaml +microsoftGraphOrg: + providerId: + group: + filter: securityEnabled eq false and mailEnabled eq true and groupTypes/any(c:c+eq+'Unified') + search: '"description:One" AND ("displayName:Video" OR "displayName:Drive")' +``` + +In addition to these groups, one additional group will be created for your organization. +All imported groups will be a child of this group. + +### Users + +There are two modes for importing users - You can import all user objects matching a `filter`. + +```yaml +microsoftGraphOrg: + providerId: + user: + filter: accountEnabled eq true and userType eq 'member' +``` + +Alternatively you can import users that are members of specific groups. +For each group matching the `search` and `filter` query, each group member will be imported. +Only direct group members will be imported, not transient users. + +```yaml +microsoftGraphOrg: + providerId: + userGroupMember: + filter: "displayName eq 'Backstage Users'" + search: '"description:One" AND ("displayName:Video" OR "displayName:Drive")' +``` + +### User photos + +By default, the photos of users will be fetched and added to each user entity. For huge organizations this may be unfeasible, as it will take a _very_ long time, and can be disabled by setting `loadPhotos` to `false`: + +```yaml +microsoftGraphOrg: + providerId: + user: + filter: ... + loadPhotos: false +``` + +## Customizing Transformation + +Ingested entities can be customized by providing custom transformers. +These can be used to completely replace the built in logic, or used to tweak it by using the default transformers (`defaultGroupTransformer`, `defaultUserTransformer` and `defaultOrganizationTransformer` +Entities can also be excluded from backstage by returning `undefined`. + +These Transformers are be registered when configuring `MicrosoftGraphOrgEntityProvider` + +```ts +builder.addEntityProvider( + MicrosoftGraphOrgEntityProvider.fromConfig(env.config, { + // ... + /* highlight-add-start */ + groupTransformer: myGroupTransformer, + userTransformer: myUserTransformer, + organizationTransformer: myOrganizationTransformer, + /* highlight-add-end */ + }), +); +``` + +When using custom transformers, you may want to customize the data returned. +Several configuration options can be provided to tweak the Microsoft Graph query to get the data you need + +```yaml +microsoftGraphOrg: + providerId: + user: + expand: manager + group: + expand: member + select: ['id', 'displayName', 'description'] +``` + +The following provides an example of each kind of transformer + +```ts +import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'; +import { + defaultGroupTransformer, + defaultUserTransformer, + defaultOrganizationTransformer, +} from '@backstage/plugin-catalog-backend-module-msgraph'; +import { GroupEntity, UserEntity } from '@backstage/catalog-model'; + +// This group transformer completely replaces the built in logic with custom logic. +export async function myGroupTransformer( + group: MicrosoftGraph.Group, + groupPhoto?: string, +): Promise<GroupEntity | undefined> { + return { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { + name: group.id!, + annotations: {}, + }, + spec: { + type: 'Microsoft Entra ID', + children: [], + }, + }; +} + +// This user transformer makes use of the built in logic, but also sets the description field +export async function myUserTransformer( + graphUser: MicrosoftGraph.User, + userPhoto?: string, +): Promise<UserEntity | undefined> { + const backstageUser = await defaultUserTransformer(graphUser, userPhoto); + + if (backstageUser) { + backstageUser.metadata.description = 'Loaded from Microsoft Entra ID'; + } + + return backstageUser; +} + +// Example organization transformer that removes the organization group completely +export async function myOrganizationTransformer( + graphOrganization: MicrosoftGraph.Organization, +): Promise<GroupEntity | undefined> { + return undefined; +} +``` + +## Troubleshooting + +### No data + +First check your logs for the message `Reading msgraph users and groups`. +If you don't see this, check you've registered the provider, and that the schedule is valid + +If you see a log entry `Read 0 msgraph users and 0 msgraph groups`, check your search and filter arguments. + +If you see the start message (`Reading msgraph users and groups`) but no end message (`Read X msgraph users and Y msgraph groups`), then it is likely the job is taking a long time due to a large volume of data. +The default behavior is to import all users and groups, which is often more data than needed. +Try importing a smaller set of data (e.g. `filter: displayName eq 'John Smith'`). + +### Authentication / Token Errors + +See [Troubleshooting Azure Identity Authentication Issues](https://aka.ms/azsdk/js/identity/troubleshoot) + +### Error while reading users from Microsoft Graph: Authorization_RequestDenied - Insufficient privileges to complete the operation + +- Make sure you've granted all the required permissions to your application registration or managed identity +- Make sure the permissions are `Application` permissions rather than `Delegated` +- If your organization has configured "Admin consent" to be required, make sure this has been granted for your application permissions +- If your group queries are returning Microsoft Teams groups, you may need to grant addition permissions (e.g. `Team.ReadBasic.All`, `TeamMember.Read.All`) +- If you've added additional `select` or `expand` fields, those may need additional permissions granted diff --git a/docs/integrations/azure/org.md b/docs/integrations/azure/org.md index 38c2e7618a..89abd4813b 100644 --- a/docs/integrations/azure/org.md +++ b/docs/integrations/azure/org.md @@ -6,6 +6,10 @@ sidebar_label: Org Data description: Importing users and groups from Microsoft Entra ID into Backstage --- +:::info +This documentation is written for [the new backend system](../../backend-system/index.md) which is the default since Backstage [version 1.24](../../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./org--old.md) instead, and [consider migrating](../../backend-system/building-backends/08-migrating.md)! +::: + The Backstage catalog can be set up to ingest organizational data - users and teams - directly from a tenant in Microsoft Entra ID via the Microsoft Graph API. @@ -39,29 +43,17 @@ catalog: timeout: PT50M ``` -Finally, register the plugin in `catalog.ts`. +:::note For large organizations, this plugin can take a long time, so be careful setting low frequency / timeouts and importing a large amount of users / groups for the first try. +::: -```ts title="packages/backend/src/plugins/catalog.ts" -/* highlight-add-next-line */ -import { MicrosoftGraphOrgEntityProvider } from '@backstage/plugin-catalog-backend-module-msgraph'; +Finally, updated your backend by adding the following line: -export default async function createPlugin( - env: PluginEnvironment, -): Promise<Router> { - const builder = await CatalogBuilder.create(env); - - /* highlight-add-start */ - builder.addEntityProvider( - MicrosoftGraphOrgEntityProvider.fromConfig(env.config, { - logger: env.logger, - scheduler: env.scheduler, - }), - ); - /* highlight-add-end */ - - // .. -} +```ts title="packages/backend/src/index.ts" +backend.add(import('@backstage/plugin-catalog-backend/alpha')); +/* highlight-add-start */ +backend.add(import('@backstage/plugin-catalog-backend-module-msgraph/alpha')); +/* highlight-add-end */ ``` ## Authenticating with Microsoft Graph @@ -146,27 +138,24 @@ microsoftGraphOrg: search: '"description:One" AND ("displayName:Video" OR "displayName:Drive")' ``` +### User photos + +By default, the photos of users will be fetched and added to each user entity. For huge organizations this may be unfeasible, as it will take a _very_ long time, and can be disabled by setting `loadPhotos` to `false`: + +```yaml +microsoftGraphOrg: + providerId: + user: + filter: ... + loadPhotos: false +``` + ## Customizing Transformation Ingested entities can be customized by providing custom transformers. These can be used to completely replace the built in logic, or used to tweak it by using the default transformers (`defaultGroupTransformer`, `defaultUserTransformer` and `defaultOrganizationTransformer` Entities can also be excluded from backstage by returning `undefined`. -These Transformers are be registered when configuring `MicrosoftGraphOrgEntityProvider` - -```ts -builder.addEntityProvider( - MicrosoftGraphOrgEntityProvider.fromConfig(env.config, { - // ... - /* highlight-add-start */ - groupTransformer: myGroupTransformer, - userTransformer: myUserTransformer, - organizationTransformer: myOrganizationTransformer, - /* highlight-add-end */ - }), -); -``` - When using custom transformers, you may want to customize the data returned. Several configuration options can be provided to tweak the Microsoft Graph query to get the data you need @@ -180,9 +169,53 @@ microsoftGraphOrg: select: ['id', 'displayName', 'description'] ``` -The following provides an example of each kind of transformer +### Using Custom Transformers -```ts +Transformers can be configured by extending `microsoftGraphOrgEntityProviderTransformExtensionPoint`. Here is an example: + +```ts title="packages/backend/src/index.ts" +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { microsoftGraphOrgEntityProviderTransformExtensionPoint } from '@backstage/plugin-catalog-backend-module-msgraph/alpha'; +import { + myUserTransformer, + myGroupTransformer, + myOrganizationTransformer, +} from './transformers'; + +backend.add( + createBackendModule({ + pluginId: 'catalog', + moduleId: 'microsoft-graph-extensions', + register(env) { + env.registerInit({ + deps: { + /* highlight-add-start */ + microsoftGraphTransformers: + microsoftGraphOrgEntityProviderTransformExtensionPoint, + /* highlight-add-end */ + }, + async init({ microsoftGraphTransformers }) { + /* highlight-add-start */ + microsoftGraphTransformers.setUserTransformer(myUserTransformer); + microsoftGraphTransformers.setGroupTransformer(myGroupTransformer); + microsoftGraphTransformers.setOrganizationTransformer( + myOrganizationTransformer, + ); + /* highlight-add-end */ + }, + }); + }, + }), +); +``` + +The `myUserTransformer`, `myGroupTransformer`, and `myOrganizationTransformer` transformer functions are from the examples in the section below. + +### Transformer Examples + +The following provides an example of each kind of transformer. We recommend creating a `transformers.ts` file in your `packages/backend/src` folder for these. + +```ts title="packages/backend/src/transformers.ts" import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'; import { defaultGroupTransformer, diff --git a/docs/integrations/bitbucketCloud/discovery.md b/docs/integrations/bitbucketCloud/discovery.md index 81770afbf8..160827cb0a 100644 --- a/docs/integrations/bitbucketCloud/discovery.md +++ b/docs/integrations/bitbucketCloud/discovery.md @@ -27,7 +27,7 @@ yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-bitbuck ### Installation with New Backend System ```ts -// optional if you want HTTP endpojnts to receive external events +// optional if you want HTTP endpoints to receive external events // backend.add(import('@backstage/plugin-events-backend/alpha')); // optional if you want to use AWS SQS instead of HTTP endpoints to receive external events // backend.add(import('@backstage/plugin-events-backend-module-aws-sqs/alpha')); diff --git a/docs/integrations/datadog-rum/installation.md b/docs/integrations/datadog-rum/installation.md index 32eb2b3de8..4dc6c24134 100644 --- a/docs/integrations/datadog-rum/installation.md +++ b/docs/integrations/datadog-rum/installation.md @@ -22,6 +22,8 @@ app: applicationId: qwerty # site: datadoghq.eu # env: 'staging' + # sessionSampleRate: 100 + # sessionReplaySampleRate: 0 ``` If your [`app-config.yaml`](https://github.com/backstage/backstage/blob/e0506af8fc54074a160fb91c83d6cae8172d3bb3/app-config.yaml#L5) file does not have this configuration, you may have to adjust your [`packages/app/public/index.html`](https://github.com/backstage/backstage/blob/e0506af8fc54074a160fb91c83d6cae8172d3bb3/packages/app/public/index.html#L69) to include the Datadog RUM `init()` section manually. diff --git a/docs/integrations/github/github-apps.md b/docs/integrations/github/github-apps.md index 96363bc719..604713a490 100644 --- a/docs/integrations/github/github-apps.md +++ b/docs/integrations/github/github-apps.md @@ -125,6 +125,7 @@ integration: - Reading software components: - `Contents`: `Read-only` + - `Commit statuses`: `Read-only` - Reading organization data: - `Members`: `Read-only` - Publishing software templates: @@ -135,7 +136,6 @@ integration: - `Pull requests`: `Read & write` - `Issues`: `Read & write` - `Workflows`: `Read & write` (if templates include GitHub workflows) - - `Commit statuses`: `Read-only` - `Variables`: `Read & write` (if templates include GitHub Action Repository Variables) - `Secrets`: `Read & write` (if templates include GitHub Action Repository Secrets) - `Environments`: `Read & write` (if templates include GitHub Environments) diff --git a/docs/integrations/harness/locations.md b/docs/integrations/harness/locations.md new file mode 100644 index 0000000000..a3d0e2cfd4 --- /dev/null +++ b/docs/integrations/harness/locations.md @@ -0,0 +1,33 @@ +--- +id: locations +title: Harness Locations +sidebar_label: Locations +description: Integrating source code stored in Harness Code into the Backstage catalog +--- + +The Harness Code integration supports loading catalog entities from a hosted repository. Entities can be added to +[static catalog configuration](../../features/software-catalog/configuration.md), +registered with the +[catalog-import](https://github.com/backstage/backstage/tree/master/plugins/catalog-import) +plugin. + +## Configuration + +To use this integration, add configuration to your root `app-config.yaml`: + +```yaml +integrations: + harness: + - host: app.harness.io + token: ${HARNESS_CODE_BEARER_TOKEN} + apiKey: ${HARNESS_CODE_APIKEY} +``` + +Directly under the `harness` key is a list of provider configurations, where you +can list the Harness instances you want to be able to fetch + +check out https://developer.harness.io/docs/platform/automation/api/add-and-manage-api-keys/ for more information + +- `host`: The host of the Harness Code instance that you want to match on. +- `token` (optional): The password or api token to authenticate with. +- `apiKey` (optional): The apiKey to authenticate with. diff --git a/docs/openapi/generate-client.md b/docs/openapi/generate-client.md index 640036960e..b8acc331e5 100644 --- a/docs/openapi/generate-client.md +++ b/docs/openapi/generate-client.md @@ -20,7 +20,7 @@ info: ### Generating your client -1. Run `yarn backstage-repo-tools schema openapi generate client --output-package <directory>`. This will create a new folder in `<directory>/src/generated` to house the generated content. +1. Run `yarn backstage-repo-tools package schema openapi generate client --client-package <directory>`. This will create a new folder in `<directory>/src/generated` to house the generated content. 2. You should use the generated files as follows, - `apis/DefaultApi.client.ts` - this is the client that you should use. It has types for all of the various operations on your API. diff --git a/docs/overview/versioning-policy.md b/docs/overview/versioning-policy.md index 0b1bc975bd..2ae73e1383 100644 --- a/docs/overview/versioning-policy.md +++ b/docs/overview/versioning-policy.md @@ -81,19 +81,21 @@ In order for Backstage to function properly the following versioning rules must be followed. The rules are referring to the [Package Architecture](https://backstage.io/docs/overview/architecture-overview#package-architecture). -- The versions of all the packages in the `Frontend App Core` must be from the - same release, and it is recommended to keep `Common Tooling` on that release - too. -- The Backstage dependencies of any given plugin should be from the same - release. This includes the packages from `Common Libraries`, - `Frontend Plugin Core`, and `Frontend Libraries`, or alternatively the - `Backend Libraries`. -- There must be no package that is from a newer release than the - `Frontend App Core` packages in the app. +- The versions of all packages for each of the "App Core" groups must be from the + same Backstage release. +- For each frontend and backend setup, the "App Core" packages must be ahead of or on the same Backstage release as the "Plugin Core" packages, including transitive dependencies of all installed plugins and modules. +- For any given plugin, the versions of all packages from the "Plugin Core" and + "Library" groups must be from the same Backstage release. - Frontend plugins with a corresponding backend plugin should be from the same release. The update to the backend plugin **MUST** be deployed before or together with the update to the frontend plugin. +It is allowed and often expected that the "Plugin Core" and "Library" packages +are from older releases than the "App Core" packages. It is also allowed to have +duplicate installations of the "Plugin Core" and "Library" packages. This is all +to make sure that upgrading Backstage is as smooth as possible and allows for +more flexibility across the entire plugin ecosystem. + ## Package Versioning Policy Every individual package is versioned according to [semver](https://semver.org). diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index 497a6be9b9..7a51cb635c 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -44,7 +44,7 @@ cd plugins/carmen-backend yarn start ``` -> Note: this documentation assumes you are using the latest version of Backstage and the new backend system. If you are not please upgrade and migrate your backend using the [Migration Guide](../backend-system/building-backends/08-migrating.md) +> Note: this documentation assumes you are using the latest version of Backstage and the new backend system. If you are not, please upgrade and migrate your backend using the [Migration Guide](../backend-system/building-backends/08-migrating.md) This will think for a bit, and then say `Listening on :7007`. In a different terminal window, now run @@ -72,38 +72,19 @@ to your backend. yarn --cwd packages/backend add @internal/plugin-carmen-backend@^0.1.0 # Change this to match the plugin's package.json ``` -Create a new file named `packages/backend/src/plugins/carmen.ts`, and add the -following to it +Update `packages/backend/src/index` with the following, ```ts -import { createRouter } from '@internal/plugin-carmen-backend'; -import { Router } from 'express'; -import { PluginEnvironment } from '../types'; +const backend = createBackend(); -export default async function createPlugin( - env: PluginEnvironment, -): Promise<Router> { - // Here is where you will add all of the required initialization code that - // your backend plugin needs to be able to start! - - // The env contains a lot of goodies, but our router currently only - // needs a logger - return await createRouter({ - logger: env.logger, - }); -} -``` - -And finally, wire this into the overall backend router. Edit -`packages/backend/src/index.ts`: - -```ts -import carmen from './plugins/carmen'; // ... -async function main() { - // ... - const carmenEnv = useHotMemoize(module, () => createEnv('carmen')); - apiRouter.use('/carmen', await carmen(carmenEnv)); + +// highlight-add-next-line +backend.add(import('@internal/plugin-carmen-backend')); + +// ... + +backend.start(); ``` After you start the backend (e.g. using `yarn start-backend` from the repo @@ -116,36 +97,123 @@ curl localhost:7007/api/carmen/health This should return `{"status":"ok"}` like before. Success! +## Secure by Default + +In 1.25, Backstage started moving to a secure by default model for plugins. This means that network requests to plugins will by default not allow unauthenticated users. Let's take a deeper look at the above curl request which should allow unauthenticated access. + +The actual endpoint that is being called is defined in + +```ts title="plugins/carmen-backend/src/service/router.ts" +export async function createRouter( + options: RouterOptions, +): Promise<express.Router> { + // ... + + // highlight-start + router.get('/health', (_, response) => { + logger.info('PONG!'); + response.json({ status: 'ok' }); + }); + // highlight-end + + // ... + return router; +} +``` + +You'll notice that there is no authentication mechanism defined here, just the route name and response data. That's because the authentication is handled in your plugin definition, + +```ts title="plugins/carmen-backend/src/plugin.ts" +httpRouter.use( + await createRouter({ + logger, + }), +); +// highlight-start +httpRouter.addAuthPolicy({ + path: '/health', + allow: 'unauthenticated', +}); +// highlight-end +``` + +This allows requests to this plugin's `/health` endpoint to go through unauthenticated! + +## Using Dependencies + +In the new backend, dependencies are defined statically during registration and then "injected" during initialization. Here's an example of what this looks like, + +```ts title="plugins/carmen-backend/src/plugin.ts" + +// highlight-start +deps: { + httpRouter: coreServices.httpRouter, + logger: coreServices.logger, +}, +// highlight-end +// And then you can use them through the options property! +// highlight-next-line +async init({ httpRouter, logger }) { + // ... +}, + +``` + +You can add your own dependencies by adding a named item to the `deps` parameter: + +```ts +deps: { + // highlight-next-line + myDependency: coreServices.rootConfig, +}, +``` + +And then you can access it by referencing it in the `init` block of your plugin definition, + +```ts +async init({ myDependency }) { + // .. +} +``` + +And then you're free to call it and pass it into your router as needed. + +Backstage provides a bunch of `coreServices` out of box, see the more in depth docs [here](../backend-system/core-services/01-index.md). + ## Making Use of a Database The Backstage backend comes with a builtin facility for SQL database access. Most plugins that have persistence needs will choose to make use of this facility, so that Backstage operators can manage database needs uniformly. -As part of the environment object that is passed to your `createPlugin` -function, there is a `database` field. You can use that to get a -[Knex](http://knexjs.org/) connection object. - -```ts -// in packages/backend/src/plugins/carmen.ts -export default async function createPlugin( - env: PluginEnvironment, -): Promise<Router> { - const db: Knex<any, unknown[]> = await env.database.getClient(); +You can access this by adding a dependency on the `coreServices.database` service. +That will give you a [Knex](http://knexjs.org/) connection object. +```ts title="plugins/carmen-backend/src/plugin.ts" +deps: { + // ... + // highlight-next-line + database: coreServices.database, +}, +async init({ + // highlight-next-line + database, +}) { // You will then pass this client into your actual plugin implementation // code, maybe similar to the following: - const model = new CarmenDatabaseModel(db); - return await createRouter({ - model: model, - logger: env.logger, - }); + const model = new CarmenDatabaseModel(database); + httpRouter.use( + await createRouter({ + // highlight-next-line + model, + logger, + }), + ); } ``` -You may note that the `getClient` call has no parameters. This is because all -plugin database needs are configured under the `backend.database` config key of -your `app-config.yaml`. The framework may even make sure behind the scenes that +All plugin database needs are configured under the `backend.database` config key +of your `app-config.yaml`. The framework may even make sure behind the scenes that the logical database is created automatically if it doesn't exist, based on rules that the Backstage operator decides on. @@ -159,37 +227,67 @@ database.. ## Making Use of the User's Identity -The Backstage backend comes with a facility for retrieving the identity of the -logged in user. +The Backstage backend also offers a core service to access the user's identity. You can access it through the `coreServices.httpAuth` and `coreServices.userInfo` dependencies. -As part of the environment object that is passed to your `createPlugin` -function, there is a `identity` field. You can use that to get an identity -from the request. - -```ts -// in packages/backend/src/plugins/carmen.ts -export default async function createPlugin( - env: PluginEnvironment, -): Promise<Router> { - return await createRouter({ - model: model, - logger: env.logger, - identity: env.identity, - }); +```ts title="plugins/carmen-backend/src/plugin.ts" +deps: { + // highlight-start + httpAuth: coreServices.httpAuth, + userInfo: coreServices.userInfo, + // highlight-end +}, +async init({ + // highlight-start + httpAuth, + userInfo, + // highlight-end +}) { + httpRouter.use( + await createRouter({ + // highlight-start + httpAuth, + userInfo, + // highlight-end + logger, + }), + ); } ``` The plugin can then extract the identity from the request. ```ts +export interface RouterOptions { + logger: LoggerService; + // highlight-start + userInfo: UserInfoService; + httpAuth: HttpAuthService; + // highlight-end +} + export async function createRouter( options: RouterOptions, ): Promise<express.Router> { - const router = Router(); - const { identity } = options; + const { userInfo, httpAuth } = options; - router.post('/example', async (req, res) => { - const userIdentity = await identity.getIdentity({ request: req }); - ... + router.post('/me', async (req, res) => { + const credentials = await httpAuth.credentials(req, { + // This rejects request from non-users. Only use this if your plugin needs to access the + // user identity, most of the time it's enough to just call `httpAuth.credentials(req)` + allow: ['user'], + }); + + const userInfo = await userInfo.getUserInfo(credentials); + + res.json({ + // The catalog entity ref of the user. + userEntityRef: userInfo.userEntityRef, + + // The list of entities that this user or any teams this user is a part of owns. + ownershipEntityRefs: userInfo.ownershipEntityRefs, + }); }); + + // ... +} ``` diff --git a/docs/plugins/index.md b/docs/plugins/index.md index 755c1b8a7d..94de3bc482 100644 --- a/docs/plugins/index.md +++ b/docs/plugins/index.md @@ -22,7 +22,7 @@ To create a plugin, follow the steps outlined [here](create-a-plugin.md). If you start developing a plugin that you aim to release as open source, we suggest that you create a -[new Issue](https://github.com/backstage/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). +[new Issue in the community plugins repo](https://github.com/backstage/community-plugins/issues/new/choose). This helps the community know what plugins are in development. You can also use this process if you have an idea for a good plugin but you hope diff --git a/docs/plugins/observability.md b/docs/plugins/observability.md index d8778f07f8..e13653706f 100644 --- a/docs/plugins/observability.md +++ b/docs/plugins/observability.md @@ -15,6 +15,32 @@ See how to install Datadog Events in your app ## Logging +### New Backend + +The backend supplies a central logging service, [`rootLogger`](../backend-system/core-services/root-logger.md), as well as a plugin based logger, [`logger`](../backend-system/core-services/logger.md) from `coreServices`. To add additional granularity to your logs, you can create children from the plugin based logger, using the `.child()` method and provide is with JSON data. For example, if you wanted to log items for a specific span in your plugin, you could do + +```ts +export function createRouter({ logger }) { + const router = Router(); + + router.post('/task/:taskId/queue', (req, res) => { + const { taskId } = req.params; + const taskLogger = logger.child({ task: taskId }); + taskLogger.log('Queueing this task.'); + }); + + router.get('/task/:taskId/results', (req, res) => { + const { taskId } = req.params; + const taskLogger = logger.child({ task: taskId }); + taskLogger.log('Getting the results of this task.'); + }); +} +``` + +You can also add additional metadata to all logs for your Backstage instance by overriding the `rootLogger` implementation, you can see an example in [the `logger` docs](../backend-system/core-services/logger.md#configuring-the-service). + +### Old Backend + The backend supplies a central [winston](https://github.com/winstonjs/winston) root logger that plugins are expected to use for their logging needs. In the default production setup, it emits structured JSON logs on stdout, with a field @@ -37,7 +63,39 @@ An example log line could look as follows: ## Health Checks -The example backend in the Backstage repository +### New Backend + +The new backend is moving towards health checks being plugin-based, as such there is no current plugin for providing a health check route. You can add this yourself easily though, + +```ts +import { + coreServices, + createBackendModule, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; + +const healthCheck = createBackendPlugin({ + pluginId: 'healthcheck', + + register(env) { + env.registerInit({ + deps: { + rootHttpRouter: coreServices.rootHttpRouter, + }, + init: async ({ rootHttpRouter }) => { + // You can adjust the route name and response as you need. + rootHttpRouter.use('/healthcheck', (req, res) => { + res.json({ status: 'ok' }); + }); + }, + }); + }, +}); +``` + +### Old Backend + +The example old backend in the Backstage repository [supplies](https://github.com/backstage/backstage/blob/bc18571b7a742863a770b2a54e785d6bbef7e184/packages/backend/src/index.ts#L99) a very basic health check endpoint on the `/healthcheck` route. You may add such a handler to your backend as well, and supply your own logic to it that fits diff --git a/docs/plugins/proxying.md b/docs/plugins/proxying.md index 40d155e13b..bff38fea40 100644 --- a/docs/plugins/proxying.md +++ b/docs/plugins/proxying.md @@ -15,6 +15,16 @@ can be the best choice for communicating with an API. The plugin is already added to a default Backstage project. +### New Backend + +To add it to a project, add the following line in `packages/backend/src/index.ts`: + +```ts +backend.add(import('@backstage/plugin-proxy-backend/alpha')); +``` + +### Old Backend + In `packages/backend/src/index.ts`: ```ts diff --git a/docs/publishing.md b/docs/publishing.md index 29853955f1..d7a4f65008 100644 --- a/docs/publishing.md +++ b/docs/publishing.md @@ -52,7 +52,7 @@ Additional steps for the main line release - Create Release Notes PR - Add the release note file as [`/docs/releases/vx.y.0.md`](./releases) - Add an entry to [`/microsite/sidebar.json`](https://github.com/backstage/backstage/blob/master/microsite/sidebars.json) for the release note - - Update the navigation bar item in [`/microsite/docusaurus.config.js`](https://github.com/backstage/backstage/blob/master/microsite/docusaurus.config.js) to point to the new release note + - Update the navigation bar item in [`/microsite/docusaurus.config.ts`](https://github.com/backstage/backstage/blob/master/microsite/docusaurus.config.ts) to point to the new release note - Finally copy the content, without the metadata header, into the description of the [`Version Packages` Pull Request](https://github.com/backstage/backstage/pulls?q=is%3Aopen+is%3Apr+in%3Atitle+%22Version+Packages) Once the release has been published edit the newly created release in the [GitHub repository](https://github.com/backstage/backstage/releases) and replace the text content with the release notes. diff --git a/docs/releases/v1.27.0-next.0-changelog.md b/docs/releases/v1.27.0-next.0-changelog.md new file mode 100644 index 0000000000..04ef3c3c2f --- /dev/null +++ b/docs/releases/v1.27.0-next.0-changelog.md @@ -0,0 +1,2064 @@ +# Release v1.27.0-next.0 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.27.0-next.0](https://backstage.github.io/upgrade-helper/?to=1.27.0-next.0) + +## @backstage/catalog-model@1.5.0-next.0 + +### Minor Changes + +- 79025f3: Introduce a domain attribute to the domain entity to allow a hierarchy of domains to exist. + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-catalog-backend@1.22.0-next.0 + +### Minor Changes + +- 8d14475: Emit well known relationships for the Domain entity kind. + +### Patch Changes + +- c6cb568: Add lifecycle monitoring for the catalog processing +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-search-backend-module-catalog@0.1.23-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-openapi-utils@0.1.11-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.3.4-next.0 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + +## @backstage/app-defaults@1.5.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/plugin-permission-react@0.4.22 + +## @backstage/backend-app-api@0.7.1-next.0 + +### Patch Changes + +- 4cd5ff0: Add ability to configure the Node.js HTTP Server when configuring the root HTTP Router service +- e8199b1: Move the JWKS registration outside of the lifecycle middleware +- dc8c5dd: The default `TokenManager` implementation no longer requires keys to be configured in production, but it will throw an errors when generating or authenticating tokens. The default `AuthService` implementation will now also provide additional context if such an error is throw when falling back to using the `TokenManager` service to generate tokens for outgoing requests. +- 025641b: Redact `meta` fields too with the logger +- 5863e02: Internal refactor to only create one external token handler +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.5 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.8.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-node@0.7.29-next.0 + +## @backstage/backend-common@0.21.8-next.0 + +### Patch Changes + +- ba0b8b4: Added option to `ServerTokenManager.fromConfig` that allows it to be instantiated in production without any configured keys. +- Updated dependencies + - @backstage/backend-app-api@0.7.1-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/backend-dev-utils@0.1.4 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.8.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + +## @backstage/backend-defaults@0.2.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.7.1-next.0 + - @backstage/backend-common@0.21.8-next.0 + +## @backstage/backend-dynamic-feature-service@0.2.10-next.0 + +### Patch Changes + +- b611fd0: Updates the `scanRoot` method in the `PluginScanner` class to specifically ignore the `lost+found` directory, which is a system-generated directory used for file recovery on Unix-like systems. Skipping this directory avoids unnecessary errors. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.22.0-next.0 + - @backstage/backend-app-api@0.7.1-next.0 + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.5 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.8.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-app-node@0.1.18-next.0 + - @backstage/plugin-events-backend@0.3.5-next.0 + - @backstage/plugin-events-node@0.3.4-next.0 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + - @backstage/plugin-search-common@1.2.11 + +## @backstage/backend-openapi-utils@0.1.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/errors@1.2.4 + +## @backstage/backend-plugin-api@0.6.18-next.0 + +### Patch Changes + +- 1fedf0c: Removed explicit `toString()` method from `ServiceRef` type. +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + +## @backstage/backend-tasks@0.5.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/backend-test-utils@0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.7.1-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/catalog-client@1.6.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/errors@1.2.4 + +## @backstage/cli@0.26.5-next.0 + +### Patch Changes + +- fcd3462: Both the target and types library have been bumped from ES2021 to ES2022 in `@backstage/cli/config/tsconfig.json`. +- 0cc5ed3: Add support for `versions:migrate` to do code changes. Can be skipped with `--no-code-changes` +- f97ad04: Add preserveModules to rollup, which allows better async loading and tree-shaking in webpack +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.5 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.8.0 + - @backstage/errors@1.2.4 + - @backstage/eslint-plugin@0.1.7 + - @backstage/integration@1.10.0 + - @backstage/release-manifests@0.0.11 + - @backstage/types@1.1.1 + +## @backstage/core-compat-api@0.2.5-next.0 + +### Patch Changes + +- 5d99272: Update local development dependencies. +- Updated dependencies + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/version-bridge@1.0.8 + +## @backstage/core-components@0.14.5-next.0 + +### Patch Changes + +- 71e4229: Added optional `initialShowDropDown` prop to `SidebarSubmenuItem` to internally manage the initial display state of the dropdown items. +- Updated dependencies + - @backstage/theme@0.5.4-next.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/version-bridge@1.0.8 + +## @backstage/create-app@0.5.15-next.0 + +### Patch Changes + +- c066c88: Removed `packages/backend/src/types.ts` from the template as it is unused. It was mistakenly left in after moving the template to the new backend system. +- 5d99272: Update local development dependencies. +- 8105aad: Removed the Tech Radar and GitHub Actions plugins from the template, which have been moved to the community plugins repository. +- Updated dependencies + - @backstage/cli-common@0.1.13 + +## @backstage/dev-utils@1.0.32-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/app-defaults@1.5.5-next.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/integration-react@1.1.26 + +## @backstage/frontend-app-api@0.6.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + +## @backstage/frontend-plugin-api@0.6.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + +## @backstage/frontend-test-utils@0.1.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-app-api@0.6.5-next.0 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/test-utils@1.5.5-next.0 + - @backstage/types@1.1.1 + +## @backstage/repo-tools@0.8.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.5 + - @backstage/config-loader@1.8.0 + - @backstage/errors@1.2.4 + +## @techdocs/cli@1.8.10-next.0 + +### Patch Changes + +- 1a0e009: Fix cookie endpoint mock for `serve` +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/plugin-techdocs-node@1.12.4-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + +## @backstage/test-utils@1.5.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.5.4-next.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-react@0.4.22 + +## @backstage/theme@0.5.4-next.0 + +### Patch Changes + +- f1462df: Fixed bug where scrollbars don't pick up the theme when in dark mode + +## @backstage/plugin-api-docs@0.11.5-next.0 + +### Patch Changes + +- 5d99272: Update local development dependencies. +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/plugin-catalog@1.19.1-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/plugin-permission-react@0.4.22 + +## @backstage/plugin-app-backend@0.3.66-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.8.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-app-node@0.1.18-next.0 + +## @backstage/plugin-app-node@0.1.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config-loader@1.8.0 + +## @backstage/plugin-app-visualizer@0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.5-next.0 + +## @backstage/plugin-auth-backend@0.22.5-next.0 + +### Patch Changes + +- ea9262b: Allow overriding default ownership resolving with the new `AuthOwnershipResolutionExtensionPoint` +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-backend-module-oidc-provider@0.1.9-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.1.1-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.1.10-next.0 + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.10-next.0 + - @backstage/plugin-auth-backend-module-bitbucket-provider@0.1.1-next.0 + - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.1.1-next.0 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.13-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.1.15-next.0 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.15-next.0 + - @backstage/plugin-auth-backend-module-google-provider@0.1.15-next.0 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.1.13-next.0 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.1.15-next.0 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.11-next.0 + - @backstage/plugin-auth-backend-module-okta-provider@0.0.11-next.0 + +## @backstage/plugin-auth-backend-module-atlassian-provider@0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + +## @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.22.5-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-auth-backend-module-bitbucket-provider@0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + +## @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-auth-backend-module-github-provider@0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + +## @backstage/plugin-auth-backend-module-gitlab-provider@0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + +## @backstage/plugin-auth-backend-module-google-provider@0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + +## @backstage/plugin-auth-backend-module-guest-provider@0.1.4-next.0 + +### Patch Changes + +- 07d8cca: Error if used outside of a development environment without explicit allowance +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-auth-backend-module-microsoft-provider@0.1.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + +## @backstage/plugin-auth-backend-module-oauth2-provider@0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + +## @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-auth-backend-module-oidc-provider@0.1.9-next.0 + +### Patch Changes + +- dd53bf3: Add nonce to authorize request to be added in ID token +- Updated dependencies + - @backstage/plugin-auth-backend@0.22.5-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + +## @backstage/plugin-auth-backend-module-okta-provider@0.0.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + +## @backstage/plugin-auth-backend-module-pinniped-provider@0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + +## @backstage/plugin-auth-backend-module-vmware-cloud-provider@0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + +## @backstage/plugin-auth-node@0.4.13-next.0 + +### Patch Changes + +- ea9262b: Allow overriding default ownership resolving with the new `AuthOwnershipResolutionExtensionPoint` +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-auth-react@0.1.1-next.0 + +### Patch Changes + +- c297afd: When using `CookieAuthRefreshProvider` or `useCookieAuthRefresh`, a 404 response from the cookie endpoint will now be treated as if cookie auth is disabled and is not needed. +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + +## @backstage/plugin-catalog@1.19.1-next.0 + +### Patch Changes + +- 5d99272: Update local development dependencies. +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-scaffolder-common@1.5.2-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/integration-react@1.1.26 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-react@0.4.22 + - @backstage/plugin-search-common@1.2.11 + - @backstage/plugin-search-react@1.7.11-next.0 + +## @backstage/plugin-catalog-backend-module-aws@0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-kubernetes-common@0.7.6-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/integration-aws-node@0.1.12 + +## @backstage/plugin-catalog-backend-module-azure@0.1.38-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.10.0 + +## @backstage/plugin-catalog-backend-module-backstage-openapi@0.2.1-next.0 + +### Patch Changes + +- f3f0281: Fix incorrect dependency import. +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-openapi-utils@0.1.11-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.2.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.10.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.18 + - @backstage/plugin-events-node@0.3.4-next.0 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.1.32-next.0 + +### Patch Changes + +- 062ffb1: Allow skipping archived repositories (`skipArchivedRepos` flag) on Bitbucket. +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + +## @backstage/plugin-catalog-backend-module-gcp@0.1.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-kubernetes-common@0.7.6-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.35-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + +## @backstage/plugin-catalog-backend-module-github@0.6.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.22.0-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.10.0 + - @backstage/plugin-events-node@0.3.4-next.0 + +## @backstage/plugin-catalog-backend-module-github-org@0.1.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-backend-module-github@0.6.1-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-events-node@0.3.4-next.0 + +## @backstage/plugin-catalog-backend-module-gitlab@0.3.15-next.2 + +### Patch Changes + +- a70377d: Added events support for `GitlabDiscoveryEntityProvider` and `GitlabOrgDiscoveryEntityProvider`. +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.10.0 + - @backstage/plugin-events-node@0.3.4-next.0 + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.0.1-next.0 + +### Patch Changes + +- a70377d: Added a new `catalog-backend-module-gitlab-org` module which adds the `GitlabOrgDiscoveryEntityProvider` to the catalog's providers using the new backend system. +- Updated dependencies + - @backstage/plugin-catalog-backend-module-gitlab@0.3.15-next.2 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-events-node@0.3.4-next.0 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.4.23-next.0 + +### Patch Changes + +- 8c1ab9e: Fix plugin/incremental-ingestion 'Maximum call stack size exceeded' error when ingest large entities. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.22.0-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-events-node@0.3.4-next.0 + - @backstage/plugin-permission-common@0.7.13 + +## @backstage/plugin-catalog-backend-module-ldap@0.5.34-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-catalog-backend-module-msgraph@0.5.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + +## @backstage/plugin-catalog-backend-module-openapi@0.1.36-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.22.0-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.10.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.1.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-scaffolder-common@1.5.2-next.0 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.1 + - @backstage/plugin-permission-common@0.7.13 + +## @backstage/plugin-catalog-common@1.0.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-search-common@1.2.11 + +## @backstage/plugin-catalog-graph@0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-catalog-import@0.10.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/integration@1.10.0 + - @backstage/integration-react@1.1.26 + +## @backstage/plugin-catalog-node@1.11.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + +## @backstage/plugin-catalog-react@1.11.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/integration-react@1.1.26 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-react@0.4.22 + +## @backstage/plugin-catalog-unprocessed-entities@0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + +## @backstage/plugin-config-schema@0.1.55-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-devtools@0.1.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/plugin-devtools-common@0.1.9 + - @backstage/plugin-permission-react@0.4.22 + +## @backstage/plugin-devtools-backend@0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.8.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-devtools-common@0.1.9 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + +## @backstage/plugin-events-backend@0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-events-node@0.3.4-next.0 + +## @backstage/plugin-events-backend-module-aws-sqs@0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.3.4-next.0 + +## @backstage/plugin-events-backend-module-azure@0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-events-node@0.3.4-next.0 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-events-node@0.3.4-next.0 + +## @backstage/plugin-events-backend-module-gerrit@0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-events-node@0.3.4-next.0 + +## @backstage/plugin-events-backend-module-github@0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-events-node@0.3.4-next.0 + +## @backstage/plugin-events-backend-module-gitlab@0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-events-node@0.3.4-next.0 + +## @backstage/plugin-events-backend-test-utils@0.1.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.3.4-next.0 + +## @backstage/plugin-events-node@0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + +## @backstage/plugin-home@0.7.4-next.0 + +### Patch Changes + +- 2196d3e: Use relative time when displaying visits from the same day +- 0040ec2: Updated dependency `@rjsf/utils` to `5.18.2`. + Updated dependency `@rjsf/core` to `5.18.2`. + Updated dependency `@rjsf/material-ui` to `5.18.2`. + Updated dependency `@rjsf/validator-ajv8` to `5.18.2`. +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-home-react@0.1.13-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.5-next.0 + +## @backstage/plugin-home-react@0.1.13-next.0 + +### Patch Changes + +- 0040ec2: Updated dependency `@rjsf/utils` to `5.18.2`. + Updated dependency `@rjsf/core` to `5.18.2`. + Updated dependency `@rjsf/material-ui` to `5.18.2`. + Updated dependency `@rjsf/validator-ajv8` to `5.18.2`. +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + +## @backstage/plugin-kubernetes@0.11.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-kubernetes-common@0.7.6-next.0 + - @backstage/plugin-kubernetes-react@0.3.5-next.0 + - @backstage/core-plugin-api@1.9.2 + +## @backstage/plugin-kubernetes-backend@0.17.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-kubernetes-node@0.1.12-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-kubernetes-common@0.7.6-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + +## @backstage/plugin-kubernetes-cluster@0.0.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-kubernetes-common@0.7.6-next.0 + - @backstage/plugin-kubernetes-react@0.3.5-next.0 + - @backstage/core-plugin-api@1.9.2 + +## @backstage/plugin-kubernetes-common@0.7.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + +## @backstage/plugin-kubernetes-node@0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-kubernetes-common@0.7.6-next.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-kubernetes-react@0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-kubernetes-common@0.7.6-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-notifications@0.2.1-next.0 + +### Patch Changes + +- e6bf85f: Allow overriding `NotificationsPage` page properties +- a42a19b: Empty descriptions are not rendered to improve the look&feel. +- 1bc3b86: Fix to show web notifications even when browser is on foreground. Fix duplicate notifications with multiple tabs. +- f793112: Allow defining `className` and additional properties for `NotificationsSideBarItem` +- e1c7d6e: Fix infinite loop in the notification title counter +- fcda449: The rendered size of a notification is limited for very long descriptions. +- Updated dependencies + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.3 + - @backstage/plugin-signals-react@0.0.3 + +## @backstage/plugin-notifications-backend@0.2.1-next.0 + +### Patch Changes + +- ee09dfc: Updated documentation for sending messages by external services. +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-notifications-node@0.1.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-events-node@0.3.4-next.0 + - @backstage/plugin-notifications-common@0.0.3 + - @backstage/plugin-signals-node@0.1.4-next.0 + +## @backstage/plugin-notifications-node@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-notifications-common@0.0.3 + - @backstage/plugin-signals-node@0.1.4-next.0 + +## @backstage/plugin-org@0.6.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.5-next.0 + +## @backstage/plugin-org-react@0.1.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/core-plugin-api@1.9.2 + +## @backstage/plugin-permission-backend@0.5.42-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + +## @backstage/plugin-permission-backend-module-allow-all-policy@0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + +## @backstage/plugin-permission-node@0.7.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.7.13 + +## @backstage/plugin-proxy-backend@0.4.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + +## @backstage/plugin-scaffolder@1.19.4-next.0 + +### Patch Changes + +- 0040ec2: Updated dependency `@rjsf/utils` to `5.18.2`. + Updated dependency `@rjsf/core` to `5.18.2`. + Updated dependency `@rjsf/material-ui` to `5.18.2`. + Updated dependency `@rjsf/validator-ajv8` to `5.18.2`. +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-scaffolder-react@1.8.5-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-scaffolder-common@1.5.2-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/integration@1.10.0 + - @backstage/integration-react@1.1.26 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-react@0.4.22 + +## @backstage/plugin-scaffolder-backend@1.22.5-next.0 + +### Patch Changes + +- 025641b: Fix issue with the log format not being respected when logging from actions +- 025641b: Redact `meta` fields too with the logger +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-github@0.2.8-next.0 + - @backstage/plugin-scaffolder-backend-module-gitea@0.1.8-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.3.4-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.8-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.16-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-scaffolder-common@1.5.2-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + - @backstage/plugin-scaffolder-backend-module-azure@0.1.10-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.2.8-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.8-next.0 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.1.10-next.0 + +## @backstage/plugin-scaffolder-backend-module-azure@0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + +## @backstage/plugin-scaffolder-backend-module-bitbucket@0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.8-next.0 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.8-next.0 + +### Patch Changes + +- 24dd655: Add examples for `publish:bitbucketServer:pull-request` scaffolder action & improve related tests +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.42-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder-backend-module-gerrit@0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + +## @backstage/plugin-scaffolder-backend-module-gitea@0.1.8-next.0 + +### Patch Changes + +- 554af73: Allow defining `repoVisibility` field for the action `publish:gitea` +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + +## @backstage/plugin-scaffolder-backend-module-github@0.2.8-next.0 + +### Patch Changes + +- 5d99272: Update local development dependencies. +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.3.4-next.0 + +### Patch Changes + +- ffc73ec: Add examples for `gitlab:repo:push` scaffolder action & improve related tests +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + +## @backstage/plugin-scaffolder-backend-module-rails@0.4.35-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.1.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node-test-utils@0.1.4-next.0 + +## @backstage/plugin-scaffolder-common@1.5.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + +## @backstage/plugin-scaffolder-node@0.4.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-common@1.5.2-next.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder-node-test-utils@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-test-utils@0.3.8-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder-react@1.8.5-next.0 + +### Patch Changes + +- 0040ec2: Updated dependency `@rjsf/utils` to `5.18.2`. + Updated dependency `@rjsf/core` to `5.18.2`. + Updated dependency `@rjsf/material-ui` to `5.18.2`. + Updated dependency `@rjsf/validator-ajv8` to `5.18.2`. +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-scaffolder-common@1.5.2-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + +## @backstage/plugin-search@1.4.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-search-common@1.2.11 + - @backstage/plugin-search-react@1.7.11-next.0 + +## @backstage/plugin-search-backend@1.5.8-next.0 + +### Patch Changes + +- c6cb568: Add lifecycle monitoring for the search index registry +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/repo-tools@0.8.1-next.0 + - @backstage/backend-openapi-utils@0.1.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + - @backstage/plugin-search-common@1.2.11 + +## @backstage/plugin-search-backend-module-catalog@0.1.23-next.0 + +### Patch Changes + +- d5fff66: Fix wiring of the module exported at the `/alpha` path, which was causing authentication failures. +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-search-common@1.2.11 + +## @backstage/plugin-search-backend-module-elasticsearch@1.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-search-common@1.2.11 + +## @backstage/plugin-search-backend-module-explore@0.1.23-next.0 + +### Patch Changes + +- ca6e2e0: Migrate search collator to use the new auth services. +- 5d99272: Update README.md to point to explore plugin in community-plugins repository. +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-search-common@1.2.11 + +## @backstage/plugin-search-backend-module-pg@0.5.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-search-common@1.2.11 + +## @backstage/plugin-search-backend-module-stack-overflow-collator@0.1.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-search-common@1.2.11 + +## @backstage/plugin-search-backend-module-techdocs@0.1.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-techdocs-node@1.12.4-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-search-common@1.2.11 + +## @backstage/plugin-search-backend-node@1.2.22-next.0 + +### Patch Changes + +- c6cb568: Add lifecycle monitoring for the search index registry +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-search-common@1.2.11 + +## @backstage/plugin-search-react@1.7.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-search-common@1.2.11 + +## @backstage/plugin-signals@0.0.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/types@1.1.1 + - @backstage/plugin-signals-react@0.0.3 + +## @backstage/plugin-signals-backend@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.3.4-next.0 + - @backstage/plugin-signals-node@0.1.4-next.0 + +## @backstage/plugin-signals-node@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.3.4-next.0 + +## @backstage/plugin-techdocs@1.10.5-next.0 + +### Patch Changes + +- d2cc139: Update path in Readme for Plugin Techdocs to show the correct setup information. +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-react@0.1.1-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-techdocs-react@1.2.4-next.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/integration@1.10.0 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-search-common@1.2.11 + - @backstage/plugin-search-react@1.7.11-next.0 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.32-next.0 + +### Patch Changes + +- 2f13862: Fix bug in TechDocsAddonTester when jest.resetAllMocks is called between tests +- Updated dependencies + - @backstage/plugin-catalog@1.19.1-next.0 + - @backstage/plugin-techdocs@1.10.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-techdocs-react@1.2.4-next.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/integration-react@1.1.26 + - @backstage/test-utils@1.5.5-next.0 + - @backstage/plugin-search-react@1.7.11-next.0 + +## @backstage/plugin-techdocs-backend@1.10.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.1.23-next.0 + - @backstage/plugin-techdocs-node@1.12.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/plugin-permission-common@0.7.13 + +## @backstage/plugin-techdocs-module-addons-contrib@1.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-techdocs-react@1.2.4-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/integration@1.10.0 + - @backstage/integration-react@1.1.26 + +## @backstage/plugin-techdocs-node@1.12.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-search-common@1.2.11 + +## @backstage/plugin-techdocs-react@1.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/version-bridge@1.0.8 + +## @backstage/plugin-user-settings@0.8.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-user-settings-backend@0.2.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## example-app@0.2.97-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications@0.2.1-next.0 + - @backstage/plugin-api-docs@0.11.5-next.0 + - @backstage/plugin-catalog@1.19.1-next.0 + - @backstage/plugin-home@0.7.4-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-techdocs@1.10.5-next.0 + - @backstage/cli@0.26.5-next.0 + - @backstage/plugin-auth-react@0.1.1-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-scaffolder-react@1.8.5-next.0 + - @backstage/plugin-scaffolder@1.19.4-next.0 + - @backstage/plugin-catalog-graph@0.4.5-next.0 + - @backstage/plugin-catalog-import@0.10.11-next.0 + - @backstage/plugin-devtools@0.1.14-next.0 + - @backstage/plugin-org@0.6.25-next.0 + - @backstage/plugin-search@1.4.11-next.0 + - @backstage/plugin-user-settings@0.8.6-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.4-next.0 + - @backstage/plugin-kubernetes@0.11.10-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.11-next.0 + - @backstage/plugin-techdocs-react@1.2.4-next.0 + - @backstage/app-defaults@1.5.5-next.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-app-api@0.6.5-next.0 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-permission-react@0.4.22 + - @backstage/plugin-search-common@1.2.11 + - @backstage/plugin-search-react@1.7.11-next.0 + - @backstage/plugin-signals@0.0.6-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.10-next.0 + +## example-app-next@0.0.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications@0.2.1-next.0 + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/plugin-api-docs@0.11.5-next.0 + - @backstage/plugin-catalog@1.19.1-next.0 + - @backstage/plugin-home@0.7.4-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-techdocs@1.10.5-next.0 + - @backstage/cli@0.26.5-next.0 + - @backstage/plugin-auth-react@0.1.1-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-scaffolder-react@1.8.5-next.0 + - @backstage/plugin-scaffolder@1.19.4-next.0 + - @backstage/plugin-catalog-graph@0.4.5-next.0 + - @backstage/plugin-catalog-import@0.10.11-next.0 + - @backstage/plugin-org@0.6.25-next.0 + - @backstage/plugin-search@1.4.11-next.0 + - @backstage/plugin-user-settings@0.8.6-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.4-next.0 + - @backstage/plugin-kubernetes@0.11.10-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.11-next.0 + - @backstage/plugin-techdocs-react@1.2.4-next.0 + - @backstage/app-defaults@1.5.5-next.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-app-api@0.6.5-next.0 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-app-visualizer@0.1.6-next.0 + - @backstage/plugin-permission-react@0.4.22 + - @backstage/plugin-search-common@1.2.11 + - @backstage/plugin-search-react@1.7.11-next.0 + - @backstage/plugin-signals@0.0.6-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.10-next.0 + +## app-next-example-plugin@0.0.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/frontend-plugin-api@0.6.5-next.0 + +## example-backend@0.2.98-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.22.0-next.0 + - @backstage/plugin-scaffolder-backend@1.22.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/plugin-search-backend@1.5.8-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.3.4-next.0 + - @backstage/plugin-search-backend-module-catalog@0.1.23-next.0 + - @backstage/plugin-search-backend-module-explore@0.1.23-next.0 + - @backstage/plugin-auth-backend@0.22.5-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - example-app@0.2.97-next.0 + - @backstage/plugin-app-backend@0.3.66-next.0 + - @backstage/plugin-kubernetes-backend@0.17.1-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.16-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.4.5-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.1.23-next.0 + - @backstage/plugin-techdocs-backend@1.10.5-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.10.0 + - @backstage/plugin-devtools-backend@0.3.4-next.0 + - @backstage/plugin-events-backend@0.3.5-next.0 + - @backstage/plugin-events-node@0.3.4-next.0 + - @backstage/plugin-permission-backend@0.5.42-next.0 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + - @backstage/plugin-proxy-backend@0.4.16-next.0 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.19-next.0 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.35-next.0 + - @backstage/plugin-search-backend-module-elasticsearch@1.4.1-next.0 + - @backstage/plugin-search-backend-module-pg@0.5.27-next.0 + - @backstage/plugin-signals-backend@0.1.4-next.0 + - @backstage/plugin-signals-node@0.1.4-next.0 + +## example-backend-next@0.0.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-github@0.2.8-next.0 + - @backstage/plugin-catalog-backend@1.22.0-next.0 + - @backstage/plugin-scaffolder-backend@1.22.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/plugin-search-backend@1.5.8-next.0 + - @backstage/plugin-search-backend-module-catalog@0.1.23-next.0 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.2.1-next.0 + - @backstage/plugin-auth-backend-module-guest-provider@0.1.4-next.0 + - @backstage/plugin-search-backend-module-explore@0.1.23-next.0 + - @backstage/plugin-auth-backend@0.22.5-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/plugin-notifications-backend@0.2.1-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-backend-module-openapi@0.1.36-next.0 + - @backstage/backend-defaults@0.2.18-next.0 + - @backstage/plugin-app-backend@0.3.66-next.0 + - @backstage/plugin-kubernetes-backend@0.17.1-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.16-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.4.5-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.1.23-next.0 + - @backstage/plugin-techdocs-backend@1.10.5-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.1.15-next.0 + - @backstage/plugin-devtools-backend@0.3.4-next.0 + - @backstage/plugin-permission-backend@0.5.42-next.0 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.1.15-next.0 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + - @backstage/plugin-proxy-backend@0.4.16-next.0 + - @backstage/plugin-signals-backend@0.1.4-next.0 + +## e2e-test@0.2.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.15-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/errors@1.2.4 + +## techdocs-cli-embedded-app@0.2.96-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.19.1-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-techdocs@1.10.5-next.0 + - @backstage/cli@0.26.5-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-techdocs-react@1.2.4-next.0 + - @backstage/app-defaults@1.5.5-next.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/integration-react@1.1.26 + - @backstage/test-utils@1.5.5-next.0 + +## @internal/plugin-todo-list@1.0.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + +## @internal/plugin-todo-list-backend@1.0.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/errors@1.2.4 diff --git a/docs/releases/v1.27.0-next.1-changelog.md b/docs/releases/v1.27.0-next.1-changelog.md new file mode 100644 index 0000000000..77c6919cda --- /dev/null +++ b/docs/releases/v1.27.0-next.1-changelog.md @@ -0,0 +1,1487 @@ +# Release v1.27.0-next.1 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.27.0-next.1](https://backstage.github.io/upgrade-helper/?to=1.27.0-next.1) + +## @backstage/backend-common@0.22.0-next.1 + +### Minor Changes + +- ed83f85: Internal refactor of the database code. + + **BREAKING**: The helper functions `createDatabaseClient` and `ensureDatabaseExists` have been removed from the public interface, since they have no usage within the repository and never were suitable for calling from the outside. Please consider using `coreServices.database` or `DatabaseManager` directly wherever possible instead. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.7.2-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/config-loader@1.8.0 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/app-defaults@1.5.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## @backstage/backend-app-api@0.7.2-next.1 + +### Patch Changes + +- 09f8988: Remove explicit `alg` check for user tokens in `verifyToken` +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/cli-node@0.2.5 + - @backstage/config-loader@1.8.0 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/backend-defaults@0.2.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.7.2-next.1 + - @backstage/backend-common@0.22.0-next.1 + +## @backstage/backend-dynamic-feature-service@0.2.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.7.2-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-catalog-backend@1.22.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-events-backend@0.3.5-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/cli-node@0.2.5 + - @backstage/config-loader@1.8.0 + - @backstage/backend-plugin-api@0.6.18-next.1 + - @backstage/plugin-app-node@0.1.18-next.1 + +## @backstage/backend-openapi-utils@0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/backend-plugin-api@0.6.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + +## @backstage/backend-tasks@0.5.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/backend-test-utils@0.3.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.7.2-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/core-compat-api@0.2.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.5-next.1 + +## @backstage/core-components@0.14.6-next.1 + +### Patch Changes + +- 5223c4c: Fixed an internal circular import that broke Jest mocks. +- 5b7b49b: The `SignInPage` guest provider will now fall back to legacy guest auth if the backend request fails, allowing guest auth without a running backend. + +## @backstage/create-app@0.5.15-next.1 + +### Patch Changes + +- Bumped create-app version. + +## @backstage/dev-utils@1.0.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/app-defaults@1.5.5-next.1 + - @backstage/integration-react@1.1.26 + +## @backstage/frontend-app-api@0.6.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + +## @backstage/frontend-plugin-api@0.6.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## @backstage/frontend-test-utils@0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-app-api@0.6.5-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + +## @backstage/repo-tools@0.8.1-next.1 + +### Patch Changes + +- 9ae9bb2: Update the paths logic in the api reports command to support complex subpaths +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/cli-node@0.2.5 + - @backstage/config-loader@1.8.0 + +## @techdocs/cli@1.8.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-techdocs-node@1.12.4-next.1 + +## @backstage/plugin-api-docs@0.11.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog@1.19.1-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## @backstage/plugin-app-backend@0.3.66-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/config-loader@1.8.0 + - @backstage/backend-plugin-api@0.6.18-next.1 + - @backstage/plugin-app-node@0.1.18-next.1 + +## @backstage/plugin-app-node@0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.8.0 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-app-visualizer@0.1.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + +## @backstage/plugin-auth-backend@0.22.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.10-next.1 + - @backstage/plugin-auth-backend-module-oidc-provider@0.1.9-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.1.10-next.1 + - @backstage/plugin-auth-backend-module-bitbucket-provider@0.1.1-next.1 + - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.1.1-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.1.15-next.1 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.15-next.1 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.1.13-next.1 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.1.15-next.1 + - @backstage/plugin-auth-backend-module-okta-provider@0.0.11-next.1 + - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.1.1-next.1 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.13-next.1 + - @backstage/plugin-auth-backend-module-google-provider@0.1.15-next.1 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.11-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-atlassian-provider@0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-backend@0.22.5-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-bitbucket-provider@0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-github-provider@0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-gitlab-provider@0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-google-provider@0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-guest-provider@0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-microsoft-provider@0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-oauth2-provider@0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-oidc-provider@0.1.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-backend@0.22.5-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-okta-provider@0.0.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-pinniped-provider@0.1.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-backend-module-vmware-cloud-provider@0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-node@0.4.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-auth-react@0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## @backstage/plugin-catalog@1.19.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.5.2-next.1 + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-search-react@1.7.11-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## @backstage/plugin-catalog-backend@1.22.0-next.1 + +### Patch Changes + +- 8479a0b: Fixed bug in stitching queue gauge that included entities that are scheduled in the future. +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.24-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + - @backstage/backend-openapi-utils@0.1.11-next.1 + +## @backstage/plugin-catalog-backend-module-aws@0.3.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-azure@0.1.38-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-backstage-openapi@0.2.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + - @backstage/backend-openapi-utils@0.1.11-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.2.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.1.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-gcp@0.1.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.35-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-github@0.6.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-catalog-backend@1.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-github-org@0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-backend-module-github@0.6.1-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-gitlab@0.3.15-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-catalog-backend-module-gitlab@0.3.15-next.3 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.4.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-catalog-backend@1.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-ldap@0.5.34-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + +## @backstage/plugin-catalog-backend-module-msgraph@0.5.26-next.1 + +### Patch Changes + +- 49eab29: Fixed disabling of user photo fetching. Previously, the config value wasn't propagated properly, so user photos was still being fetched despite disabled by config. +- 6e370e6: Handle fetching huge amounts of users from Azure without crashing +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-openapi@0.1.36-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-catalog-backend@1.22.0-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.5.2-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.4.5-next.1 + +### Patch Changes + +- 6f5a3a3: Correctly convert owner to string in case owner has not been provided +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-graph@0.4.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## @backstage/plugin-catalog-import@0.10.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/core-compat-api@0.2.5-next.1 + +## @backstage/plugin-catalog-node@1.11.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-catalog-react@1.11.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + +## @backstage/plugin-catalog-unprocessed-entities@0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## @backstage/plugin-config-schema@0.1.55-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## @backstage/plugin-devtools@0.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## @backstage/plugin-devtools-backend@0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/config-loader@1.8.0 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-events-backend@0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-events-backend-module-aws-sqs@0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-events-backend-module-azure@0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-events-backend-module-gerrit@0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-events-backend-module-github@0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-events-backend-module-gitlab@0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-events-backend-test-utils@0.1.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.3.4-next.1 + +## @backstage/plugin-events-node@0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-home@0.7.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/plugin-home-react@0.1.13-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## @backstage/plugin-home-react@0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## @backstage/plugin-kubernetes@0.11.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-kubernetes-react@0.3.5-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + +## @backstage/plugin-kubernetes-backend@0.17.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-kubernetes-node@0.1.12-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-kubernetes-cluster@0.0.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-kubernetes-react@0.3.5-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + +## @backstage/plugin-kubernetes-node@0.1.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-kubernetes-react@0.3.5-next.1 + +### Patch Changes + +- 3102a99: add the namespace label to CronJobDrawer & IngressDrawer. +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## @backstage/plugin-notifications@0.2.1-next.1 + +### Patch Changes + +- f730c0b: The user can newly mark all unread messages as read at one click. +- bfcb2f1: Allow showing notifications as snackbars in the UI +- f6633ca: Add option to set the notification as read automatically when the notification link is opened +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## @backstage/plugin-notifications-backend@0.2.1-next.1 + +### Patch Changes + +- 295c05d: Support for filtering entities from notification recipients after resolving them from the recipients +- cba628a: Add possibility to generate random notifications on the fly in local development +- Updated dependencies + - @backstage/plugin-notifications-node@0.1.4-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-signals-node@0.1.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-notifications-backend-module-email@0.0.1-next.0 + +### Patch Changes + +- dbf2696: Allow sending notifications by email with the new notifications module +- Updated dependencies + - @backstage/plugin-notifications-node@0.1.4-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-notifications-node@0.1.4-next.1 + +### Patch Changes + +- 295c05d: Support for filtering entities from notification recipients after resolving them from the recipients +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-signals-node@0.1.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-org@0.6.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## @backstage/plugin-org-react@0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + +## @backstage/plugin-permission-backend@0.5.42-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-permission-backend-module-allow-all-policy@0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-permission-node@0.7.29-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-proxy-backend@0.4.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder@1.19.4-next.1 + +### Patch Changes + +- 9156654: Capturing more event clicks for scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.5.2-next.1 + - @backstage/plugin-scaffolder-react@1.8.5-next.1 + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/core-compat-api@0.2.5-next.1 + +## @backstage/plugin-scaffolder-backend@1.22.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.5.2-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.16-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.2.8-next.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.3.4-next.1 + - @backstage/plugin-scaffolder-backend-module-azure@0.1.10-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.2.8-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.8-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.8-next.1 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.1.10-next.1 + - @backstage/plugin-scaffolder-backend-module-gitea@0.1.8-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-backend-module-azure@0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-backend-module-bitbucket@0.2.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.8-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.8-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.42-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-backend-module-gerrit@0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-backend-module-gitea@0.1.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-backend-module-github@0.2.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-backend-module-rails@0.4.35-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.1.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.3.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/plugin-scaffolder-node-test-utils@0.1.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-common@1.5.2-next.1 + +### Patch Changes + +- 9156654: Capturing more event clicks for scaffolder + +## @backstage/plugin-scaffolder-node@0.4.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.5.2-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-scaffolder-node-test-utils@0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-test-utils@0.3.8-next.1 + +## @backstage/plugin-scaffolder-react@1.8.5-next.1 + +### Patch Changes + +- 9156654: Capturing more event clicks for scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.5.2-next.1 + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + +## @backstage/plugin-search@1.4.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/plugin-search-react@1.7.11-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## @backstage/plugin-search-backend@1.5.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/repo-tools@0.8.1-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + - @backstage/backend-openapi-utils@0.1.11-next.1 + +## @backstage/plugin-search-backend-module-catalog@0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-search-backend-module-elasticsearch@1.4.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-search-backend-module-explore@0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-search-backend-module-pg@0.5.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-search-backend-module-stack-overflow-collator@0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-search-backend-module-techdocs@0.1.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/plugin-techdocs-node@1.12.4-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-search-backend-node@1.2.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-search-react@1.7.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + +## @backstage/plugin-signals@0.0.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## @backstage/plugin-signals-backend@0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-signals-node@0.1.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-signals-node@0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-techdocs@1.10.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-auth-react@0.1.2-next.1 + - @backstage/plugin-search-react@1.7.11-next.1 + - @backstage/plugin-techdocs-react@1.2.4-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.19.1-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-search-react@1.7.11-next.1 + - @backstage/plugin-techdocs@1.10.5-next.1 + - @backstage/plugin-techdocs-react@1.2.4-next.1 + +## @backstage/plugin-techdocs-backend@1.10.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.1.23-next.1 + - @backstage/plugin-techdocs-node@1.12.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-techdocs-module-addons-contrib@1.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-techdocs-react@1.2.4-next.1 + +## @backstage/plugin-techdocs-node@1.12.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/plugin-techdocs-react@1.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## @backstage/plugin-user-settings@0.8.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## @backstage/plugin-user-settings-backend@0.2.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## example-app@0.2.97-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.8.5-next.1 + - @backstage/plugin-scaffolder@1.19.4-next.1 + - @backstage/plugin-notifications@0.2.1-next.1 + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog@1.19.1-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/app-defaults@1.5.5-next.1 + - @backstage/cli@0.26.5-next.0 + - @backstage/frontend-app-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-api-docs@0.11.5-next.1 + - @backstage/plugin-auth-react@0.1.2-next.1 + - @backstage/plugin-catalog-graph@0.4.5-next.1 + - @backstage/plugin-catalog-import@0.10.11-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.2.4-next.1 + - @backstage/plugin-devtools@0.1.14-next.1 + - @backstage/plugin-home@0.7.4-next.1 + - @backstage/plugin-kubernetes@0.11.10-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.11-next.1 + - @backstage/plugin-org@0.6.25-next.1 + - @backstage/plugin-search@1.4.11-next.1 + - @backstage/plugin-search-react@1.7.11-next.1 + - @backstage/plugin-signals@0.0.6-next.1 + - @backstage/plugin-techdocs@1.10.5-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.10-next.1 + - @backstage/plugin-techdocs-react@1.2.4-next.1 + - @backstage/plugin-user-settings@0.8.6-next.1 + +## example-app-next@0.0.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.8.5-next.1 + - @backstage/plugin-scaffolder@1.19.4-next.1 + - @backstage/plugin-notifications@0.2.1-next.1 + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog@1.19.1-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/app-defaults@1.5.5-next.1 + - @backstage/cli@0.26.5-next.0 + - @backstage/frontend-app-api@0.6.5-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-api-docs@0.11.5-next.1 + - @backstage/plugin-app-visualizer@0.1.6-next.1 + - @backstage/plugin-auth-react@0.1.2-next.1 + - @backstage/plugin-catalog-graph@0.4.5-next.1 + - @backstage/plugin-catalog-import@0.10.11-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.2.4-next.1 + - @backstage/plugin-home@0.7.4-next.1 + - @backstage/plugin-kubernetes@0.11.10-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.11-next.1 + - @backstage/plugin-org@0.6.25-next.1 + - @backstage/plugin-search@1.4.11-next.1 + - @backstage/plugin-search-react@1.7.11-next.1 + - @backstage/plugin-signals@0.0.6-next.1 + - @backstage/plugin-techdocs@1.10.5-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.10-next.1 + - @backstage/plugin-techdocs-react@1.2.4-next.1 + - @backstage/plugin-user-settings@0.8.6-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## app-next-example-plugin@0.0.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + +## example-backend@0.2.98-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-unprocessed@0.4.5-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-catalog-backend@1.22.0-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.16-next.1 + - @backstage/plugin-scaffolder-backend@1.22.5-next.1 + - example-app@0.2.97-next.1 + - @backstage/plugin-search-backend@1.5.8-next.1 + - @backstage/plugin-app-backend@0.3.66-next.1 + - @backstage/plugin-kubernetes-backend@0.17.1-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-auth-backend@0.22.5-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-devtools-backend@0.3.4-next.1 + - @backstage/plugin-events-backend@0.3.5-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-permission-backend@0.5.42-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/plugin-proxy-backend@0.4.16-next.1 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.19-next.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.3.4-next.1 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.35-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.24-next.1 + - @backstage/plugin-search-backend-module-elasticsearch@1.4.1-next.1 + - @backstage/plugin-search-backend-module-explore@0.1.24-next.1 + - @backstage/plugin-search-backend-module-pg@0.5.27-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.1.23-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/plugin-signals-backend@0.1.4-next.1 + - @backstage/plugin-signals-node@0.1.4-next.1 + - @backstage/plugin-techdocs-backend@1.10.5-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + +## example-backend-next@0.0.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-unprocessed@0.4.5-next.1 + - @backstage/plugin-notifications-backend@0.2.1-next.1 + - @backstage/plugin-catalog-backend@1.22.0-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.16-next.1 + - @backstage/plugin-scaffolder-backend@1.22.5-next.1 + - @backstage/plugin-search-backend@1.5.8-next.1 + - @backstage/backend-defaults@0.2.18-next.1 + - @backstage/plugin-app-backend@0.3.66-next.1 + - @backstage/plugin-kubernetes-backend@0.17.1-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-auth-backend@0.22.5-next.1 + - @backstage/plugin-auth-backend-module-guest-provider@0.1.4-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-catalog-backend-module-openapi@0.1.36-next.1 + - @backstage/plugin-devtools-backend@0.3.4-next.1 + - @backstage/plugin-permission-backend@0.5.42-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/plugin-proxy-backend@0.4.16-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.2.8-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.24-next.1 + - @backstage/plugin-search-backend-module-explore@0.1.24-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.1.23-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/plugin-signals-backend@0.1.4-next.1 + - @backstage/plugin-techdocs-backend@1.10.5-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.1.15-next.1 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.1.15-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.2.1-next.1 + +## e2e-test@0.2.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.15-next.1 + +## techdocs-cli-embedded-app@0.2.96-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog@1.19.1-next.1 + - @backstage/app-defaults@1.5.5-next.1 + - @backstage/cli@0.26.5-next.0 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-techdocs@1.10.5-next.1 + - @backstage/plugin-techdocs-react@1.2.4-next.1 + +## @internal/plugin-todo-list@1.0.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## @internal/plugin-todo-list-backend@1.0.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 diff --git a/microsite/blog/2020-09-30-plugin-marketplace.mdx b/microsite/blog/2020-09-30-plugin-marketplace.mdx index 2f74b0200e..f97c744263 100644 --- a/microsite/blog/2020-09-30-plugin-marketplace.mdx +++ b/microsite/blog/2020-09-30-plugin-marketplace.mdx @@ -34,7 +34,7 @@ This grand vision is actually not that far off. Already today there is a growing Not all plugins you need will be open source. Every company has their own homegrown tooling. Building internal plugins lets you tailor your version of Backstage to be a perfect fit for your infrastructure and software development needs. If you end up [building plugins](https://backstage.io/docs/plugins/create-a-plugin) that could be useful for other companies, please consider releasing them as open source and [add them to the Marketplace](https://backstage.io/docs/plugins/add-to-marketplace). -If you start developing a plugin that you aim to release as open source, we suggest that you create a [new plugin Issue](https://github.com/backstage/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). This helps the community know what plugins are in development and opens up opportunities for collaboration. You can also use this process if you have an idea for a good plugin, but you need help building it. +If you start developing a plugin that you aim to release as open source, we suggest that you create a [new plugin Issue on the community plugins repo](https://github.com/backstage/community-plugins/issues/new/choose). This helps the community know what plugins are in development and opens up opportunities for collaboration. You can also use this process if you have an idea for a good plugin, but you need help building it. We are really excited to see all the amazing plugins that have already been built, and look forward to seeing even more ideas and collaboration as the Backstage community continues to grow. diff --git a/microsite/blog/2021-06-22-spotify-backstage-is-growing.mdx b/microsite/blog/2021-06-22-spotify-backstage-is-growing.mdx index 6cae5c907e..308effaeab 100644 --- a/microsite/blog/2021-06-22-spotify-backstage-is-growing.mdx +++ b/microsite/blog/2021-06-22-spotify-backstage-is-growing.mdx @@ -66,9 +66,9 @@ Speaking of reviewers and maintainersâ€Ļ ## Adding reviewers and maintainers -[![GitHub logo](assets/21-06-22/gh-reviewers.png)](https://github.com/backstage/backstage/blob/master/GOVERNANCE.md#reviewers) +[![GitHub logo](assets/21-06-22/gh-reviewers.png)](https://github.com/backstage/community/blob/main/GOVERNANCE.md#reviewers) -We have introduced [reviewers](https://github.com/backstage/backstage/blob/master/GOVERNANCE.md#reviewers) to the project! By adding this new role, we’ve expanded the number of people who are permitted to approve and merge pull requests. This will offload some of the review work from the maintainers, simplifying and speeding up the review process for contributors. +We have introduced [reviewers](https://github.com/backstage/community/blob/main/GOVERNANCE.md#reviewers) to the project! By adding this new role, we’ve expanded the number of people who are permitted to approve and merge pull requests. This will offload some of the review work from the maintainers, simplifying and speeding up the review process for contributors. Of course, with these new efforts, we expect even more companies to adopt Backstage, which means the platform will continue to grow, and the number of PRs will continue to grow with it. As that happens, we hope to add to both the maintainer and reviewer teams in the future. diff --git a/microsite/blog/2023-04-26-kubecon-eu-2023.mdx b/microsite/blog/2023-04-26-kubecon-eu-2023.mdx index 7ee549c088..8f369c2d14 100644 --- a/microsite/blog/2023-04-26-kubecon-eu-2023.mdx +++ b/microsite/blog/2023-04-26-kubecon-eu-2023.mdx @@ -21,7 +21,7 @@ On Tuesday the Backstage maintainers hosted a jam-packed project meeting. The co ![Patrik and Ben onstage for the State of Backstage talk](assets/2023-04-26/IMG_0120.png) -Core maintainers [Ben Lambert](https://github.com/benjdlambert) and [Patrik Oldsberg](https://github.com/Rugvip) took center stage on Wednesday for the Backstage Maintainer Track: State of Backstage in 2023 talk. Backstage has officially hit over 1,000 adopters and 1,000 contributors – so it’s apt timing to modernize the governance model for the project. Taking pointers from the [CNCF Contributor Ladder Governance Template](https://contribute.cncf.io/maintainers/templates/), a new [Backstage Governance Model](https://github.com/backstage/backstage/blob/master/GOVERNANCE.md) is now in effect! Patrik walked us through the new ladder model which introduces a number of changes, one being the addition of [project area maintainers](https://github.com/backstage/backstage/blob/master/GOVERNANCE.md#project-area-maintainer). This role lets members of the community take increased ownership over a specific area of interest, like Catalog, Discoverability, TechDocs, Helm Charts, and Kubernetes. New project areas that will be added include Permissions and Software Templates. New project areas can be proposed by nominating a project area maintainer for the area. The new model also adds an organization member role for contributors who want to take a more active role in the Backstage community. You can open an issue to become an organization member [here](https://github.com/backstage/community/issues/new/choose). +Core maintainers [Ben Lambert](https://github.com/benjdlambert) and [Patrik Oldsberg](https://github.com/Rugvip) took center stage on Wednesday for the Backstage Maintainer Track: State of Backstage in 2023 talk. Backstage has officially hit over 1,000 adopters and 1,000 contributors – so it’s apt timing to modernize the governance model for the project. Taking pointers from the [CNCF Contributor Ladder Governance Template](https://contribute.cncf.io/maintainers/templates/), a new [Backstage Governance Model](https://github.com/backstage/community/blob/main/GOVERNANCE.md) is now in effect! Patrik walked us through the new ladder model which introduces a number of changes, one being the addition of [project area maintainers](https://github.com/backstage/community/blob/main/GOVERNANCE.md#project-area-maintainer). This role lets members of the community take increased ownership over a specific area of interest, like Catalog, Discoverability, TechDocs, Helm Charts, and Kubernetes. New project areas that will be added include Permissions and Software Templates. New project areas can be proposed by nominating a project area maintainer for the area. The new model also adds an organization member role for contributors who want to take a more active role in the Backstage community. You can open an issue to become an organization member [here](https://github.com/backstage/community/issues/new/choose). ![Contributor ladder](assets/2023-04-26/contributor_ladder.png) diff --git a/microsite/blog/2024-04-19-community-plugins.mdx b/microsite/blog/2024-04-19-community-plugins.mdx index bb8b9c2313..49d1765bce 100644 --- a/microsite/blog/2024-04-19-community-plugins.mdx +++ b/microsite/blog/2024-04-19-community-plugins.mdx @@ -14,7 +14,7 @@ For those who depended on these plugins, migrating is as simple as `yarn backsta ## The community plugins repo -Some of you who have been around a while, or have seen our [Maintainer Track talks](https://www.youtube.com/watch?v=ONMBYnhxnNU) at KubeCon, might have seen [this RFC](https://github.com/backstage/backstage/issues/20266) which outlines some issues with the scale of the `backstage/backstage` monorepo, and us as maintainers being the de facto owners of all plugins without a [project area](https://github.com/backstage/backstage/blob/master/GOVERNANCE.md#project-area) or [plugin maintainer](https://github.com/backstage/backstage/blob/master/GOVERNANCE.md#project-area-maintainer). +Some of you who have been around a while, or have seen our [Maintainer Track talks](https://www.youtube.com/watch?v=ONMBYnhxnNU) at KubeCon, might have seen [this RFC](https://github.com/backstage/backstage/issues/20266) which outlines some issues with the scale of the `backstage/backstage` monorepo, and us as maintainers being the de facto owners of all plugins without a [project area](https://github.com/backstage/community/blob/main/GOVERNANCE.md#project-area) or [plugin maintainer](https://github.com/backstage/community/blob/main/GOVERNANCE.md#project-area-maintainer). There was some great discussion in this issue, and some great ideas. One of the ideas was to create a dedicated home for community plugins, with all the burden of release tooling and workspace tooling already set up, which is a pretty big barrier for people wanting to create plugins for Backstage in their own organization or personal account. These plugins would then have the ability to release independently of the main monorepo, and have their own release cadence, which is something that we've been looking at exploring for a while. diff --git a/microsite/data/plugins/airbrake.yaml b/microsite/data/plugins/airbrake.yaml index 7f946597ef..8facf13185 100644 --- a/microsite/data/plugins/airbrake.yaml +++ b/microsite/data/plugins/airbrake.yaml @@ -4,7 +4,7 @@ author: Simply Business authorUrl: https://github.com/simplybusiness/ category: Monitoring description: Access Airbrake error monitoring and other integrations from within Backstage -documentation: https://github.com/backstage/backstage/blob/master/plugins/airbrake +documentation: https://github.com/backstage/community-plugins/tree/main/workspaces/airbrake/plugins/airbrake iconUrl: https://wp-assets.airbrake.io/wp-content/uploads/2020/10/05222904/Square-white-A-on-Orange.png npmPackageName: '@backstage/plugin-airbrake' addedDate: '2022-01-10' diff --git a/microsite/data/plugins/analytics-module-ga.yaml b/microsite/data/plugins/analytics-module-ga.yaml index 54dea6454a..968b1831ab 100644 --- a/microsite/data/plugins/analytics-module-ga.yaml +++ b/microsite/data/plugins/analytics-module-ga.yaml @@ -4,7 +4,7 @@ author: Spotify authorUrl: https://github.com/spotify category: Monitoring description: Track usage of your Backstage instance using Google Analytics. -documentation: https://github.com/backstage/backstage/blob/master/plugins/analytics-module-ga/README.md +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/analytics/plugins/analytics-module-ga/README.md iconUrl: /img/ga-icon.png npmPackageName: '@backstage/plugin-analytics-module-ga' addedDate: '2021-10-07' diff --git a/microsite/data/plugins/apollo-explorer.yaml b/microsite/data/plugins/apollo-explorer.yaml index 98008e3fc0..cebf36654e 100644 --- a/microsite/data/plugins/apollo-explorer.yaml +++ b/microsite/data/plugins/apollo-explorer.yaml @@ -4,7 +4,7 @@ author: unredundant authorUrl: https://github.com/unredundant category: Debugging description: Integrates Apollo Explorer graphs as a tool to browse GraphQL API endpoints inside Backstage. -documentation: https://github.com/backstage/backstage/blob/master/plugins/apollo-explorer/README.md +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/apollo-explorer/plugins/apollo-explorer/README.md iconUrl: /img/apollo-explorer.png npmPackageName: '@backstage/plugin-apollo-explorer' addedDate: '2022-07-20' diff --git a/microsite/data/plugins/aws-amazon-ecs.yaml b/microsite/data/plugins/aws-amazon-ecs.yaml new file mode 100644 index 0000000000..a94779babd --- /dev/null +++ b/microsite/data/plugins/aws-amazon-ecs.yaml @@ -0,0 +1,10 @@ +--- +title: Amazon Elastic Container Service +author: Amazon Web Services +authorUrl: https://aws.amazon.com/ +category: Infrastructure +description: View resources related to Amazon Elastic Container Service for your components in Backstage. +documentation: https://github.com/awslabs/backstage-plugins-for-aws/tree/main/plugins/ecs#readme +iconUrl: https://raw.githubusercontent.com/awslabs/backstage-plugins-for-aws/main/docs/images/logos/amazon-ecs-logo.png +npmPackageName: 'https://www.npmjs.com/package/@aws/amazon-ecs-plugin-for-backstage' +addedDate: '2024-04-22' diff --git a/microsite/data/plugins/aws-codebuild.yaml b/microsite/data/plugins/aws-codebuild.yaml new file mode 100644 index 0000000000..2c12f92dce --- /dev/null +++ b/microsite/data/plugins/aws-codebuild.yaml @@ -0,0 +1,10 @@ +--- +title: AWS CodeBuild +author: Amazon Web Services +authorUrl: https://aws.amazon.com/ +category: CI/CD +description: View resources related to AWS CodeBuild for your components in Backstage. +documentation: https://github.com/awslabs/backstage-plugins-for-aws/tree/main/plugins/codebuild#readme +iconUrl: https://raw.githubusercontent.com/awslabs/backstage-plugins-for-aws/main/docs/images/logos/aws-codebuild-logo.png +npmPackageName: 'https://www.npmjs.com/package/@aws/aws-codebuild-plugin-for-backstage' +addedDate: '2024-04-22' diff --git a/microsite/data/plugins/aws-codepipeline.yaml b/microsite/data/plugins/aws-codepipeline.yaml new file mode 100644 index 0000000000..16986b6718 --- /dev/null +++ b/microsite/data/plugins/aws-codepipeline.yaml @@ -0,0 +1,10 @@ +--- +title: AWS CodePipeline +author: Amazon Web Services +authorUrl: https://aws.amazon.com/ +category: CI/CD +description: View resources related to AWS CodePipeline for your components in Backstage. +documentation: https://github.com/awslabs/backstage-plugins-for-aws/tree/main/plugins/codepipeline#readme +iconUrl: https://raw.githubusercontent.com/awslabs/backstage-plugins-for-aws/main/docs/images/logos/aws-codepipeline-logo.png +npmPackageName: 'https://www.npmjs.com/package/@aws/aws-codepipeline-plugin-for-backstage' +addedDate: '2024-04-22' diff --git a/microsite/data/plugins/aws-codeservices.yaml b/microsite/data/plugins/aws-codeservices.yaml deleted file mode 100644 index 34fa335923..0000000000 --- a/microsite/data/plugins/aws-codeservices.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: AWS Code Services -author: Amazon Web Services -authorUrl: https://aws.amazon.com/ -category: CI/CD -description: View resources related to AWS CodePipeline, AWS CodeBuild and AWS CodeDeploy for your components in Backstage. -documentation: https://github.com/awslabs/aws-codeservices-plugins-for-backstage#readme -iconUrl: https://github.com/awslabs/aws-codeservices-plugins-for-backstage/blob/main/docs/images/codepipeline-logo.png?raw=true -npmPackageName: '@aws/aws-codeservices-plugin-for-backstage' -addedDate: '2023-01-17' diff --git a/microsite/data/plugins/aws-proton.yaml b/microsite/data/plugins/aws-proton.yaml deleted file mode 100644 index f66b556d42..0000000000 --- a/microsite/data/plugins/aws-proton.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: AWS Proton -author: Amazon Web Services -authorUrl: https://aws.amazon.com/ -category: Infrastructure -description: Create and view AWS Proton services for your components in Backstage. -documentation: https://github.com/awslabs/aws-proton-plugins-for-backstage#readme -iconUrl: https://github.com/awslabs/aws-proton-plugins-for-backstage/blob/main/docs/images/proton-logo.png?raw=true -npmPackageName: '@aws/aws-proton-plugin-for-backstage' -addedDate: '2022-06-21' diff --git a/microsite/data/plugins/azure-pipelines.yaml b/microsite/data/plugins/azure-pipelines.yaml index 5b719a4248..e0e3534fdb 100644 --- a/microsite/data/plugins/azure-pipelines.yaml +++ b/microsite/data/plugins/azure-pipelines.yaml @@ -4,7 +4,7 @@ author: Keyloop authorUrl: https://keyloop.com/ category: CI/CD description: Easily view your Azure Pipelines within the Software Catalog -documentation: https://github.com/backstage/backstage/blob/master/plugins/azure-devops/README.md +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/azure-devops/plugins/azure-devops/README.md iconUrl: /img/azure-pipelines.svg npmPackageName: '@backstage/plugin-azure-devops' addedDate: '2021-12-22' diff --git a/microsite/data/plugins/badges.yaml b/microsite/data/plugins/badges.yaml index 86d16ab547..789aaf155d 100644 --- a/microsite/data/plugins/badges.yaml +++ b/microsite/data/plugins/badges.yaml @@ -4,7 +4,7 @@ author: Andreas Stenius authorUrl: https://github.com/backstage/community category: Discovery description: The badges plugin offers a set of badges that can be used outside of Backstage, showing information related to data from the catalog. -documentation: https://github.com/backstage/backstage/blob/master/plugins/badges/README.md +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/badges/plugins/badges/README.md iconUrl: /img/badges.svg npmPackageName: '@backstage/plugin-badges' addedDate: '2021-09-29' diff --git a/microsite/data/plugins/bazaar.yaml b/microsite/data/plugins/bazaar.yaml index ce3a4d5dfc..1130822e77 100644 --- a/microsite/data/plugins/bazaar.yaml +++ b/microsite/data/plugins/bazaar.yaml @@ -3,7 +3,7 @@ author: Axis Communications AB authorUrl: https://www.axis.com category: Discovery description: A marketplace where engineers can propose projects suitable for inner sourcing -documentation: https://github.com/backstage/backstage/blob/master/plugins/bazaar/README.md +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/bazaar/plugins/bazaar/README.md iconUrl: /img/bazaar.svg npmPackageName: '@backstage/plugin-bazaar' addedDate: '2022-01-11' diff --git a/microsite/data/plugins/betterscan.yaml b/microsite/data/plugins/betterscan.yaml index 92d1eaa7b2..852ae0081c 100644 --- a/microsite/data/plugins/betterscan.yaml +++ b/microsite/data/plugins/betterscan.yaml @@ -4,7 +4,7 @@ author: Marcin Kozlowski authorUrl: https://betterscan.io category: Security description: View security scanned vulnerabilities in Code and Cloud scanned using Open Source and proprietary scanners directly in Backstage. -documentation: https://github.com/marcinguy/betterscan-ce +documentation: https://www.npmjs.com/package/@marcinguy/backstage-plugin-betterscan iconUrl: https://uploads-ssl.webflow.com/6339e3b81867539b5fe2498d/633a1643dcb06d3029867161_g4.svg npmPackageName: '@marcinguy/backstage-plugin-betterscan' addedDate: '2022-12-08' diff --git a/microsite/data/plugins/bitrise.yaml b/microsite/data/plugins/bitrise.yaml index 5e5b74f3ad..5cbfe5aabf 100644 --- a/microsite/data/plugins/bitrise.yaml +++ b/microsite/data/plugins/bitrise.yaml @@ -4,7 +4,7 @@ author: SDA SE authorUrl: https://sda.se/ category: CI/CD description: View Bitrise builds and download the build artifacts within Backstage. -documentation: https://github.com/backstage/backstage/blob/master/plugins/bitrise/README.md +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/bitrise/plugins/bitrise/README.md iconUrl: https://avatars.githubusercontent.com/u/7174390?s=400&v=4 npmPackageName: '@backstage/plugin-bitrise' addedDate: '2021-03-01' diff --git a/microsite/data/plugins/firehydrant.yaml b/microsite/data/plugins/firehydrant.yaml index 8a1f122051..7aa00675b4 100644 --- a/microsite/data/plugins/firehydrant.yaml +++ b/microsite/data/plugins/firehydrant.yaml @@ -4,7 +4,7 @@ author: FireHydrant authorUrl: https://firehydrant.io/ category: Monitoring description: View service incidents information from FireHydrant, such as active incidents and incident metrics, directly within Backstage. -documentation: https://github.com/backstage/backstage/blob/master/plugins/firehydrant/README.md -iconUrl: https://raw.githubusercontent.com/backstage/backstage/master/plugins/firehydrant/doc/firehydrant_logo.png +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/firehydrant/plugins/firehydrant/README.md +iconUrl: https://github.com/backstage/community-plugins/blob/main/workspaces/firehydrant/plugins/firehydrant/doc/firehydrant_logo.png npmPackageName: '@backstage/plugin-firehydrant' addedDate: '2021-08-18' diff --git a/microsite/data/plugins/fossa.yaml b/microsite/data/plugins/fossa.yaml index e46228a848..a1a66eb7d6 100644 --- a/microsite/data/plugins/fossa.yaml +++ b/microsite/data/plugins/fossa.yaml @@ -4,7 +4,7 @@ author: SDA SE authorUrl: https://sda.se/ category: Quality description: View FOSSA license compliance of your components in Backstage. -documentation: https://github.com/backstage/backstage/blob/master/plugins/fossa/README.md +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/fossa/plugins/fossa/README.md iconUrl: https://avatars0.githubusercontent.com/u/9543448?s=400&v=4 npmPackageName: '@backstage/plugin-fossa' addedDate: '2020-12-10' diff --git a/microsite/data/plugins/github-codespaces.yaml b/microsite/data/plugins/github-codespaces.yaml index d846235bd6..043be3200b 100644 --- a/microsite/data/plugins/github-codespaces.yaml +++ b/microsite/data/plugins/github-codespaces.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/adityasinghal26 category: Development description: Integrates GitHub Codespaces for a Backstage component with the Authenticated User. documentation: https://github.com/adityasinghal26/backstage-plugins/tree/main/plugins/github-codespaces -iconUrl: https://github.com/adityasinghal26/backstage-plugins/blob/00c9c00ba9acc3135014d6454ccf04f573195eef/plugins/github-codespaces/images/GitHubLogo.png +iconUrl: https://github.com/adityasinghal26/backstage-plugins/blob/main/plugins/github-codespaces/images/GitHubLogo.png npmPackageName: '@adityasinghal26/plugin-github-codespaces' tags: - github diff --git a/microsite/data/plugins/linguist.yaml b/microsite/data/plugins/linguist.yaml index d76936391c..fb8e88b747 100644 --- a/microsite/data/plugins/linguist.yaml +++ b/microsite/data/plugins/linguist.yaml @@ -4,7 +4,7 @@ author: Keyloop authorUrl: https://keyloop.com/ category: Metadata description: View the programming language break down for your entities within the Software Catalog -documentation: https://github.com/backstage/backstage/blob/master/plugins/linguist/README.md +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/linguist/plugins/linguist/README.md iconUrl: /img/linguist.svg npmPackageName: '@backstage/plugin-linguist' addedDate: '2023-06-17' diff --git a/microsite/data/plugins/mia-platform.yaml b/microsite/data/plugins/mia-platform.yaml new file mode 100644 index 0000000000..d4a7b692cd --- /dev/null +++ b/microsite/data/plugins/mia-platform.yaml @@ -0,0 +1,13 @@ +--- +title: Mia Platform Sync +author: Mia-Platform +authorUrl: https://mia-platform.eu +category: Discovery +description: Elevate your workflow with our plugin that seamlessly integrates your Mia-Platform components into Backstage. Keep your information synchronized effortlessly and gain insights into your company's domains, projects, deployment environments, and repository links, all while exploring their interrelations. +documentation: https://github.com/mia-platform/backstage-plugin/blob/main/README.md +iconUrl: /img/mia-platform.png +npmPackageName: '@mia-platform/backstage-plugin-frontend' +tags: + - platform + - discovery +addedDate: '2024-04-12' diff --git a/microsite/data/plugins/newrelic-dashboard.yaml b/microsite/data/plugins/newrelic-dashboard.yaml index 420c506b35..e53285cca7 100644 --- a/microsite/data/plugins/newrelic-dashboard.yaml +++ b/microsite/data/plugins/newrelic-dashboard.yaml @@ -4,7 +4,7 @@ author: '@mufaddal7' authorUrl: https://github.com/mufaddal7 category: Monitoring description: Easily view your New Relic Dashboards in Backstage, via real-time snapshots of your dashboards -documentation: https://github.com/backstage/community-plugins/tree/main/workspaces/newrelic-dashboard/plugins/newrelic-dashboard +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/newrelic/plugins/newrelic-dashboard/README.md iconUrl: https://newrelic.com/themes/custom/erno/assets/mediakit/new_relic_logo_vertical_white.svg npmPackageName: '@backstage-community/plugin-newrelic-dashboard' tags: diff --git a/microsite/data/plugins/octopus-deploy.yaml b/microsite/data/plugins/octopus-deploy.yaml index 1ed7439695..cfc5e2c9e5 100644 --- a/microsite/data/plugins/octopus-deploy.yaml +++ b/microsite/data/plugins/octopus-deploy.yaml @@ -4,7 +4,7 @@ author: jmezach authorUrl: https://jmezach.github.io/ category: CI/CD description: Easily view your Octopus Deploy releases within the Software Catalog -documentation: https://github.com/backstage/backstage/blob/master/plugins/octopus-deploy/README.md +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/octopus-deploy/plugins/octopus-deploy/README.md iconUrl: /img/octopus-deploy.svg npmPackageName: '@backstage/plugin-octopus-deploy' addedDate: '2023-02-24' diff --git a/microsite/data/plugins/opencost.yaml b/microsite/data/plugins/opencost.yaml index 950b0606a7..95973e3b0c 100644 --- a/microsite/data/plugins/opencost.yaml +++ b/microsite/data/plugins/opencost.yaml @@ -4,7 +4,7 @@ author: Matt Ray authorUrl: https://opencost.io category: Monitoring description: OpenCost provides cloud cost monitoring for your cloud native environments. -documentation: https://github.com/backstage/backstage/blob/master/plugins/opencost/README.md +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/opencost/plugins/opencost/README.md iconUrl: /img/opencost.png npmPackageName: '@backstage/plugin-opencost' addedDate: '2023-10-26' diff --git a/microsite/data/plugins/puppetdb.yaml b/microsite/data/plugins/puppetdb.yaml index d3b6bd2bcb..650d8318d3 100644 --- a/microsite/data/plugins/puppetdb.yaml +++ b/microsite/data/plugins/puppetdb.yaml @@ -4,7 +4,7 @@ author: TDabasinskas authorUrl: https://github.com/tdabasinskas category: Configuration Management description: Visualize resource information and Puppet facts from PuppetDB. -documentation: https://github.com/backstage/backstage/blob/master/plugins/puppetdb/README.md +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/puppetdb/plugins/puppetdb/README.md iconUrl: /img/puppet.png npmPackageName: '@backstage/plugin-puppetdb' tags: diff --git a/microsite/data/plugins/shortcuts.yaml b/microsite/data/plugins/shortcuts.yaml index 7c3f2c6d56..b420e28e2f 100644 --- a/microsite/data/plugins/shortcuts.yaml +++ b/microsite/data/plugins/shortcuts.yaml @@ -4,7 +4,7 @@ author: Spotify authorUrl: https://github.com/spotify category: Utility description: The shortcuts plugin allows a user to have easy access to pages within a Backstage app by storing them as "shortcuts" in the Sidebar. -documentation: https://github.com/backstage/backstage/blob/master/plugins/shortcuts/README.md +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/shortcuts/plugins/shortcuts/README.md iconUrl: /img/shortcuts.svg npmPackageName: '@backstage/plugin-shortcuts' addedDate: '2021-10-06' diff --git a/microsite/data/plugins/sonarqube.yaml b/microsite/data/plugins/sonarqube.yaml index f24441c46e..72c662e2fa 100644 --- a/microsite/data/plugins/sonarqube.yaml +++ b/microsite/data/plugins/sonarqube.yaml @@ -4,7 +4,7 @@ author: SDA SE authorUrl: https://sda.se/ category: Quality description: Components to display code quality metrics from SonarCloud and SonarQube. -documentation: https://github.com/backstage/backstage/blob/master/plugins/sonarqube/README.md +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/sonarqube/plugins/sonarqube/README.md iconUrl: /img/sonarqube-icon.svg npmPackageName: '@backstage/plugin-sonarqube' addedDate: '2020-11-03' diff --git a/microsite/data/plugins/stack-overflow.yaml b/microsite/data/plugins/stack-overflow.yaml index a5e2122f8a..b058ce90fe 100644 --- a/microsite/data/plugins/stack-overflow.yaml +++ b/microsite/data/plugins/stack-overflow.yaml @@ -4,7 +4,7 @@ author: Spotify authorUrl: https://github.com/spotify category: Discovery description: Provides Stack Overflow specific functionality that can be used in different ways (e.g. for homepage and search) to compose your Backstage App. -documentation: https://github.com/backstage/backstage/blob/master/plugins/stack-overflow +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/stack-overflow/plugins/stack-overflow/README.md iconUrl: /img/stack-overflow-logo.svg npmPackageName: '@backstage/plugin-stack-overflow' addedDate: '2022-06-14' diff --git a/microsite/docusaurus.config.js b/microsite/docusaurus.config.js deleted file mode 100644 index 1866deda5c..0000000000 --- a/microsite/docusaurus.config.js +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// @ts-check - -/** @type{import('prism-react-renderer').PrismTheme} **/ -// @ts-ignore -const prismTheme = require('prism-react-renderer/themes/vsDark'); -prismTheme.plain.backgroundColor = '#232323'; - -/** @type {import('@docusaurus/types').Config} */ -module.exports = { - title: 'Backstage Software Catalog and Developer Platform', - tagline: 'An open source framework for building developer portals', - url: 'https://backstage.io', - baseUrl: '/', - organizationName: 'Spotify', - projectName: 'backstage', - scripts: [ - 'https://buttons.github.io/buttons.js', - 'https://unpkg.com/medium-zoom@1.0.6/dist/medium-zoom.min.js', - '/js/medium-zoom.js', - '/js/dismissable-banner.js', - '/js/scroll-nav-to-view-in-docs.js', - ], - stylesheets: [ - 'https://fonts.googleapis.com/css?family=IBM+Plex+Mono:500,700&display=swap', - ], - favicon: 'img/favicon.ico', - customFields: { - fossWebsite: 'https://spotify.github.io/', - repoUrl: 'https://github.com/backstage/backstage', - }, - onBrokenLinks: 'log', - onBrokenMarkdownLinks: 'log', - presets: [ - [ - '@docusaurus/preset-classic', - /** @type {import('@docusaurus/preset-classic').Options} */ - { - docs: { - editUrl: 'https://github.com/backstage/backstage/edit/master/docs/', - path: '../docs', - sidebarPath: 'sidebars.json', - }, - blog: { - path: 'blog', - }, - theme: { - customCss: 'src/theme/customTheme.scss', - }, - gtag: { - trackingID: 'G-KSEVGGNCJW', - }, - }, - ], - ], - markdown: { - preprocessor({ fileContent }) { - // Replace all HTML comments with empty strings as these are not supported by MDXv2. - return fileContent.replace(/<!--.*?-->/gs, ''); - }, - format: 'detect', - }, - webpack: { - jsLoader: isServer => ({ - loader: require.resolve('swc-loader'), - options: { - jsc: { - parser: { - syntax: 'typescript', - tsx: true, - }, - target: 'es2017', - }, - module: { - type: isServer ? 'commonjs' : 'es6', - }, - }, - }), - }, - plugins: [ - 'docusaurus-plugin-sass', - () => ({ - name: 'yaml-loader', - configureWebpack() { - return { - module: { - rules: [ - { - test: /\.ya?ml$/, - use: 'yaml-loader', - }, - ], - }, - }; - }, - }), - [ - '@docusaurus/plugin-client-redirects', - { - redirects: [ - { - from: '/docs', - to: '/docs/overview/what-is-backstage', - }, - { - from: '/docs/features/software-catalog/software-catalog-overview', - to: '/docs/features/software-catalog/', - }, - { - from: '/docs/features/software-templates/software-templates-index', - to: '/docs/features/software-templates/', - }, - { - from: '/docs/features/techdocs/techdocs-overview', - to: '/docs/features/techdocs/', - }, - { - from: '/docs/features/kubernetes/overview', - to: '/docs/features/kubernetes/', - }, - { - from: '/docs/features/search/search-overview', - to: '/docs/features/search/', - }, - { - from: '/docs/getting-started/running-backstage-locally', - to: '/docs/getting-started/', - }, - { - from: '/docs/features/software-templates/testing-scaffolder-alpha', - to: '/docs/features/software-templates/migrating-to-rjsf-v5', - }, - { - from: '/docs/auth/glossary', - to: '/docs/references/glossary', - }, - { - from: '/docs/overview/glossary', - to: '/docs/references/glossary', - }, - { - from: '/docs/getting-started/create-an-app', - to: '/docs/getting-started/', - }, - { - from: '/docs/getting-started/configuration', - to: '/docs/getting-started/#next-steps', - }, - ], - }, - ], - [ - 'docusaurus-pushfeedback', - { - project: 'q8w1i6cair', - hideIcon: true, - customFont: true, - buttonStyle: 'dark', - }, - ], - ], - themeConfig: - /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ - { - colorMode: { - defaultMode: 'dark', - disableSwitch: true, - }, - navbar: { - logo: { - alt: 'Backstage Software Catalog and Developer Platform', - src: 'img/logo.svg', - }, - items: [ - { - href: 'https://github.com/backstage/backstage', - label: 'GitHub', - position: 'left', - }, - { - to: 'docs/overview/what-is-backstage', - label: 'Docs', - position: 'left', - }, - { - to: '/plugins', - label: 'Plugins', - position: 'left', - }, - { - to: '/blog', - label: 'Blog', - position: 'left', - }, - { - to: 'docs/releases/v1.26.0', - label: 'Releases', - position: 'left', - }, - { - to: '/demos', - label: 'Demos', - position: 'left', - }, - { - to: '/community', - label: 'Community', - position: 'left', - }, - ], - }, - image: 'img/sharing-opengraph.png', - footer: { - links: [ - { - items: [ - { - html: ` - <a href="/" aria-label="Backstage Home"> - <div class="footerLogo"></div> - </a>`, - }, - ], - }, - { - title: 'Docs', - items: [ - { - label: 'What is Backstage?', - to: 'docs/overview/what-is-backstage', - }, - { - label: 'Getting started', - to: 'docs/getting-started/', - }, - { - label: 'Software Catalog', - to: 'docs/features/software-catalog/', - }, - { - label: 'Create a Plugin', - to: 'docs/plugins/create-a-plugin', - }, - { - label: 'Designing for Backstage', - to: 'docs/dls/design', - }, - ], - }, - { - title: 'Community', - items: [ - { - label: 'Support chatroom', - to: 'https://discord.gg/backstage-687207715902193673', - }, - { - label: 'Contributing', - to: 'https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md', - }, - { - label: 'Adopting', - to: 'https://backstage.spotify.com', - }, - { - label: 'Subscribe to our newsletter', - to: 'https://info.backstage.spotify.com/newsletter_subscribe', - }, - { - label: 'CNCF Incubation', - to: 'https://www.cncf.io/projects/', - }, - ], - }, - { - title: 'More', - items: [ - { - label: 'Open Source @ Spotify', - to: 'https://spotify.github.io/', - }, - { - label: 'Spotify Engineering Blog', - to: 'https://engineering.atspotify.com/', - }, - { - label: 'Spotify for Developers', - to: 'https://developer.spotify.com/', - }, - { - label: 'GitHub', - to: 'https://github.com/backstage/', - }, - ], - }, - ], - copyright: - '<p style="text-align:center"><a href="https://spotify.github.io/">Made with â¤ī¸ at Spotify</a></p><p class="copyright">Copyright Š 2024 Backstage Project Authors. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page: https://www.linuxfoundation.org/trademark-usage</p>', - }, - algolia: { - apiKey: '1f0ba86672ccfc3576faa94583e5b318', - indexName: 'crawler_Backstage Docusaurus 2', - appId: 'JCMFNHCHI8', - searchParameters: {}, - }, - prism: { - theme: prismTheme, - // Supported languages: https://prismjs.com/#supported-languages - // Default languages: https://github.com/FormidableLabs/prism-react-renderer/blob/master/packages/generate-prism-languages/index.ts#L9-L23 - additionalLanguages: ['docker', 'bash'], - magicComments: [ - // Extend the default highlight class name - { - className: 'code-block-highlight-line', - line: 'highlight-next-line', - block: { start: 'highlight-start', end: 'highlight-end' }, - }, - { - className: 'code-block-add-line', - line: 'highlight-add-next-line', - block: { start: 'highlight-add-start', end: 'highlight-add-end' }, - }, - { - className: 'code-block-remove-line', - line: 'highlight-remove-next-line', - block: { - start: 'highlight-remove-start', - end: 'highlight-remove-end', - }, - }, - ], - }, - }, -}; diff --git a/microsite/docusaurus.config.ts b/microsite/docusaurus.config.ts new file mode 100644 index 0000000000..5f6dfd3bc6 --- /dev/null +++ b/microsite/docusaurus.config.ts @@ -0,0 +1,357 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @ts-check + +/** @type{import('prism-react-renderer').PrismTheme} **/ +// @ts-ignore +import { themes } from 'prism-react-renderer'; +import type * as Preset from '@docusaurus/preset-classic'; +import { Config } from '@docusaurus/types'; +const backstageTheme = themes.vsDark; +backstageTheme.plain.backgroundColor = '#232323'; + +const config: Config = { + title: 'Backstage Software Catalog and Developer Platform', + tagline: 'An open source framework for building developer portals', + url: 'https://backstage.io', + baseUrl: '/', + organizationName: 'Spotify', + projectName: 'backstage', + scripts: [ + 'https://buttons.github.io/buttons.js', + 'https://unpkg.com/medium-zoom@1.0.6/dist/medium-zoom.min.js', + '/js/medium-zoom.js', + '/js/dismissable-banner.js', + '/js/scroll-nav-to-view-in-docs.js', + ], + stylesheets: [ + 'https://fonts.googleapis.com/css?family=IBM+Plex+Mono:500,700&display=swap', + ], + favicon: 'img/favicon.ico', + customFields: { + fossWebsite: 'https://spotify.github.io/', + repoUrl: 'https://github.com/backstage/backstage', + }, + onBrokenLinks: 'log', + onBrokenMarkdownLinks: 'log', + presets: [ + [ + '@docusaurus/preset-classic', + /** @type {import('@docusaurus/preset-classic').Options} */ + { + docs: { + editUrl: 'https://github.com/backstage/backstage/edit/master/docs/', + path: '../docs', + sidebarPath: 'sidebars.json', + }, + blog: { + path: 'blog', + }, + theme: { + customCss: 'src/theme/customTheme.scss', + }, + gtag: { + trackingID: 'G-KSEVGGNCJW', + }, + }, + ], + ], + markdown: { + preprocessor({ fileContent }) { + // Replace all HTML comments with empty strings as these are not supported by MDXv2. + function removeHtmlComments(input) { + let previous; + do { + previous = input; + input = input.replace(/<!--.*?-->/gs, ''); + } while (input !== previous); + return input; + } + return removeHtmlComments(fileContent); + }, + format: 'detect', + }, + webpack: { + jsLoader: isServer => ({ + loader: require.resolve('swc-loader'), + options: { + jsc: { + parser: { + syntax: 'typescript', + tsx: true, + }, + target: 'es2017', + }, + module: { + type: isServer ? 'commonjs' : 'es6', + }, + }, + }), + }, + plugins: [ + 'docusaurus-plugin-sass', + () => ({ + name: 'yaml-loader', + configureWebpack() { + return { + module: { + rules: [ + { + test: /\.ya?ml$/, + use: 'yaml-loader', + }, + ], + }, + }; + }, + }), + [ + '@docusaurus/plugin-client-redirects', + { + redirects: [ + { + from: '/docs', + to: '/docs/overview/what-is-backstage', + }, + { + from: '/docs/features/software-catalog/software-catalog-overview', + to: '/docs/features/software-catalog/', + }, + { + from: '/docs/features/software-templates/software-templates-index', + to: '/docs/features/software-templates/', + }, + { + from: '/docs/features/techdocs/techdocs-overview', + to: '/docs/features/techdocs/', + }, + { + from: '/docs/features/kubernetes/overview', + to: '/docs/features/kubernetes/', + }, + { + from: '/docs/features/search/search-overview', + to: '/docs/features/search/', + }, + { + from: '/docs/getting-started/running-backstage-locally', + to: '/docs/getting-started/', + }, + { + from: '/docs/features/software-templates/testing-scaffolder-alpha', + to: '/docs/features/software-templates/migrating-to-rjsf-v5', + }, + { + from: '/docs/auth/glossary', + to: '/docs/references/glossary', + }, + { + from: '/docs/overview/glossary', + to: '/docs/references/glossary', + }, + { + from: '/docs/getting-started/create-an-app', + to: '/docs/getting-started/', + }, + { + from: '/docs/getting-started/configuration', + to: '/docs/getting-started/#next-steps', + }, + ], + }, + ], + [ + 'docusaurus-pushfeedback', + { + project: 'q8w1i6cair', + hideIcon: true, + customFont: true, + buttonStyle: 'dark', + }, + ], + ], + themeConfig: { + colorMode: { + defaultMode: 'dark', + disableSwitch: true, + }, + navbar: { + logo: { + alt: 'Backstage Software Catalog and Developer Platform', + src: 'img/logo.svg', + }, + items: [ + { + href: 'https://github.com/backstage/backstage', + label: 'GitHub', + position: 'left', + }, + { + to: 'docs/overview/what-is-backstage', + label: 'Docs', + position: 'left', + }, + { + to: '/plugins', + label: 'Plugins', + position: 'left', + }, + { + to: '/blog', + label: 'Blog', + position: 'left', + }, + { + to: 'docs/releases/v1.26.0', + label: 'Releases', + position: 'left', + }, + { + to: '/demos', + label: 'Demos', + position: 'left', + }, + { + to: '/community', + label: 'Community', + position: 'left', + }, + ], + }, + image: 'img/sharing-opengraph.png', + footer: { + links: [ + { + items: [ + { + html: ` + <a href="/" aria-label="Backstage Home"> + <div class="footerLogo"></div> + </a>`, + }, + ], + }, + { + title: 'Docs', + items: [ + { + label: 'What is Backstage?', + to: 'docs/overview/what-is-backstage', + }, + { + label: 'Getting started', + to: 'docs/getting-started/', + }, + { + label: 'Software Catalog', + to: 'docs/features/software-catalog/', + }, + { + label: 'Create a Plugin', + to: 'docs/plugins/create-a-plugin', + }, + { + label: 'Designing for Backstage', + to: 'docs/dls/design', + }, + ], + }, + { + title: 'Community', + items: [ + { + label: 'Support chatroom', + to: 'https://discord.gg/backstage-687207715902193673', + }, + { + label: 'Contributing', + to: 'https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md', + }, + { + label: 'Adopting', + to: 'https://backstage.spotify.com', + }, + { + label: 'Subscribe to our newsletter', + to: 'https://info.backstage.spotify.com/newsletter_subscribe', + }, + { + label: 'CNCF Incubation', + to: 'https://www.cncf.io/projects/', + }, + ], + }, + { + title: 'More', + items: [ + { + label: 'Open Source @ Spotify', + to: 'https://spotify.github.io/', + }, + { + label: 'Spotify Engineering Blog', + to: 'https://engineering.atspotify.com/', + }, + { + label: 'Spotify for Developers', + to: 'https://developer.spotify.com/', + }, + { + label: 'GitHub', + to: 'https://github.com/backstage/', + }, + ], + }, + ], + copyright: `<p style="text-align:center"><a href="https://spotify.github.io/">Made with â¤ī¸ at Spotify</a></p><p class="copyright">Copyright Š ${new Date().getFullYear()} Backstage Project Authors. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page: https://www.linuxfoundation.org/trademark-usage</p>`, + }, + algolia: { + apiKey: '1f0ba86672ccfc3576faa94583e5b318', + indexName: 'crawler_Backstage Docusaurus 2', + appId: 'JCMFNHCHI8', + searchParameters: {}, + }, + prism: { + theme: backstageTheme, + // Supported languages: https://prismjs.com/#supported-languages + // Default languages: https://github.com/FormidableLabs/prism-react-renderer/blob/master/packages/generate-prism-languages/index.ts#L9-L23 + additionalLanguages: ['docker', 'bash'], + magicComments: [ + // Extend the default highlight class name + { + className: 'code-block-highlight-line', + line: 'highlight-next-line', + block: { start: 'highlight-start', end: 'highlight-end' }, + }, + { + className: 'code-block-add-line', + line: 'highlight-add-next-line', + block: { start: 'highlight-add-start', end: 'highlight-add-end' }, + }, + { + className: 'code-block-remove-line', + line: 'highlight-remove-next-line', + block: { + start: 'highlight-remove-start', + end: 'highlight-remove-end', + }, + }, + ], + }, + } satisfies Preset.ThemeConfig, +}; +export default config; diff --git a/microsite/package.json b/microsite/package.json index 1be5272286..edc4c08612 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -22,12 +22,14 @@ "@docusaurus/core": "^3.1.1", "@docusaurus/plugin-client-redirects": "^3.1.1", "@docusaurus/preset-classic": "^3.1.1", + "@docusaurus/types": "^3.1.1", + "@mdx-js/react": "^3.0.0", "@swc/core": "^1.3.46", "clsx": "^2.0.0", "docusaurus-plugin-sass": "^0.2.3", "docusaurus-pushfeedback": "^1.0.0", "luxon": "^3.0.0", - "prism-react-renderer": "^1.3.5", + "prism-react-renderer": "^2.1.0", "react": "^18.0.0", "react-dom": "^18.0.0", "sass": "^1.57.1", @@ -35,8 +37,8 @@ }, "devDependencies": { "@docusaurus/module-type-aliases": "^3.1.1", + "@docusaurus/tsconfig": "^3.1.1", "@spotify/prettier-config": "^15.0.0", - "@tsconfig/docusaurus": "^2.0.0", "@types/luxon": "^3.0.0", "@types/webpack-env": "^1.18.0", "js-yaml": "^4.1.0", diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 32863c61db..eadcbbf806 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -238,6 +238,11 @@ "label": "Gitea", "items": ["integrations/gitea/locations"] }, + { + "type": "category", + "label": "Harness", + "items": ["integrations/harness/locations"] + }, { "type": "category", "label": "Google GCS", diff --git a/microsite/static/img/mia-platform.png b/microsite/static/img/mia-platform.png new file mode 100644 index 0000000000..9ee5fa96b7 Binary files /dev/null and b/microsite/static/img/mia-platform.png differ diff --git a/microsite/tsconfig.json b/microsite/tsconfig.json index ae8fe2298a..71bc403643 100644 --- a/microsite/tsconfig.json +++ b/microsite/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@tsconfig/docusaurus/tsconfig.json", + "extends": "@docusaurus/tsconfig", "compilerOptions": { "baseUrl": ".", "types": [ diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 3b56f4c9da..3ca3e2837a 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -2160,7 +2160,14 @@ __metadata: languageName: node linkType: hard -"@docusaurus/types@npm:3.2.1": +"@docusaurus/tsconfig@npm:^3.1.1": + version: 3.2.1 + resolution: "@docusaurus/tsconfig@npm:3.2.1" + checksum: ea3c28b79b0de069c50f7b3a67d3ff682b6ded2ef02d2c7a4c2eaeddc8fcf79c9d9f5e60fbd2966cf3d247fbb8f63897b80a61fdd8b485c745a12eb684ae241a + languageName: node + linkType: hard + +"@docusaurus/types@npm:3.2.1, @docusaurus/types@npm:^3.1.1": version: 3.2.1 resolution: "@docusaurus/types@npm:3.2.1" dependencies: @@ -2689,90 +2696,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-darwin-arm64@npm:1.4.15" +"@swc/core-darwin-arm64@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-darwin-arm64@npm:1.5.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-darwin-x64@npm:1.4.15" +"@swc/core-darwin-x64@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-darwin-x64@npm:1.5.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.4.15" +"@swc/core-linux-arm-gnueabihf@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.5.0" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-arm64-gnu@npm:1.4.15" +"@swc/core-linux-arm64-gnu@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-arm64-gnu@npm:1.5.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-arm64-musl@npm:1.4.15" +"@swc/core-linux-arm64-musl@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-arm64-musl@npm:1.5.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-x64-gnu@npm:1.4.15" +"@swc/core-linux-x64-gnu@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-x64-gnu@npm:1.5.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-x64-musl@npm:1.4.15" +"@swc/core-linux-x64-musl@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-x64-musl@npm:1.5.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-win32-arm64-msvc@npm:1.4.15" +"@swc/core-win32-arm64-msvc@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-win32-arm64-msvc@npm:1.5.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-win32-ia32-msvc@npm:1.4.15" +"@swc/core-win32-ia32-msvc@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-win32-ia32-msvc@npm:1.5.0" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-win32-x64-msvc@npm:1.4.15" +"@swc/core-win32-x64-msvc@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-win32-x64-msvc@npm:1.5.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.46": - version: 1.4.15 - resolution: "@swc/core@npm:1.4.15" + version: 1.5.0 + resolution: "@swc/core@npm:1.5.0" dependencies: - "@swc/core-darwin-arm64": 1.4.15 - "@swc/core-darwin-x64": 1.4.15 - "@swc/core-linux-arm-gnueabihf": 1.4.15 - "@swc/core-linux-arm64-gnu": 1.4.15 - "@swc/core-linux-arm64-musl": 1.4.15 - "@swc/core-linux-x64-gnu": 1.4.15 - "@swc/core-linux-x64-musl": 1.4.15 - "@swc/core-win32-arm64-msvc": 1.4.15 - "@swc/core-win32-ia32-msvc": 1.4.15 - "@swc/core-win32-x64-msvc": 1.4.15 + "@swc/core-darwin-arm64": 1.5.0 + "@swc/core-darwin-x64": 1.5.0 + "@swc/core-linux-arm-gnueabihf": 1.5.0 + "@swc/core-linux-arm64-gnu": 1.5.0 + "@swc/core-linux-arm64-musl": 1.5.0 + "@swc/core-linux-x64-gnu": 1.5.0 + "@swc/core-linux-x64-musl": 1.5.0 + "@swc/core-win32-arm64-msvc": 1.5.0 + "@swc/core-win32-ia32-msvc": 1.5.0 + "@swc/core-win32-x64-msvc": 1.5.0 "@swc/counter": ^0.1.2 "@swc/types": ^0.1.5 peerDependencies: @@ -2801,7 +2808,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: b5aad99711b56ac14bb05674a54f487cffbe7fb902c14af5102c0d790be3a9a5b37bded8759d6917df37a2767bdc60133322b67ea72c5dc2796d2959dd678dd3 + checksum: 1abab0b57755020b60ebf2557629c23c65c3546e4cd80f9d02fbdf208d4169c9300a0229c088081b43a01e630c9d86d02f8c240c9fc2842eaed2a517770fcd1d languageName: node linkType: hard @@ -2842,13 +2849,6 @@ __metadata: languageName: node linkType: hard -"@tsconfig/docusaurus@npm:^2.0.0": - version: 2.0.3 - resolution: "@tsconfig/docusaurus@npm:2.0.3" - checksum: d8245a64bf131daa0c287649cb9c37ad8fecb2aedea18e15047e0df6463916e5012af3755bd73ecf90999ace1f14a3748a8b3e041d5158e2a85c22a539c649c9 - languageName: node - linkType: hard - "@types/acorn@npm:^4.0.0": version: 4.0.6 resolution: "@types/acorn@npm:4.0.6" @@ -3847,9 +3847,11 @@ __metadata: "@docusaurus/module-type-aliases": ^3.1.1 "@docusaurus/plugin-client-redirects": ^3.1.1 "@docusaurus/preset-classic": ^3.1.1 + "@docusaurus/tsconfig": ^3.1.1 + "@docusaurus/types": ^3.1.1 + "@mdx-js/react": ^3.0.0 "@spotify/prettier-config": ^15.0.0 "@swc/core": ^1.3.46 - "@tsconfig/docusaurus": ^2.0.0 "@types/luxon": ^3.0.0 "@types/webpack-env": ^1.18.0 clsx: ^2.0.0 @@ -3858,7 +3860,7 @@ __metadata: js-yaml: ^4.1.0 luxon: ^3.0.0 prettier: ^2.6.2 - prism-react-renderer: ^1.3.5 + prism-react-renderer: ^2.1.0 react: ^18.0.0 react-dom: ^18.0.0 sass: ^1.57.1 @@ -9583,16 +9585,7 @@ __metadata: languageName: node linkType: hard -"prism-react-renderer@npm:^1.3.5": - version: 1.3.5 - resolution: "prism-react-renderer@npm:1.3.5" - peerDependencies: - react: ">=0.14.9" - checksum: c18806dcbc4c0b4fd6fd15bd06b4f7c0a6da98d93af235c3e970854994eb9b59e23315abb6cfc29e69da26d36709a47e25da85ab27fed81b6812f0a52caf6dfa - languageName: node - linkType: hard - -"prism-react-renderer@npm:^2.3.0": +"prism-react-renderer@npm:^2.1.0, prism-react-renderer@npm:^2.3.0": version: 2.3.1 resolution: "prism-react-renderer@npm:2.3.1" dependencies: diff --git a/package.json b/package.json index 7ccbce417d..768448402f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "root", - "version": "1.26.0", + "version": "1.27.0-next.1", "private": true, "repository": { "type": "git", @@ -84,6 +84,7 @@ }, "prettier": "@spotify/prettier-config", "resolutions": { + "@changesets/assemble-release-plan@^6.0.0": "patch:@changesets/assemble-release-plan@npm%3A6.0.0#./.yarn/patches/@changesets-assemble-release-plan-npm-6.0.0-f7b3005037.patch", "@material-ui/pickers@^3.2.10": "patch:@material-ui/pickers@npm%3A3.3.11#./.yarn/patches/@material-ui-pickers-npm-3.3.11-1c8f68ea20.patch", "@material-ui/pickers@^3.3.10": "patch:@material-ui/pickers@npm%3A3.3.11#./.yarn/patches/@material-ui-pickers-npm-3.3.11-1c8f68ea20.patch", "@types/react": "^18", diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md index 7d7e199aca..15f1c5bd67 100644 --- a/packages/app-defaults/CHANGELOG.md +++ b/packages/app-defaults/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/app-defaults +## 1.5.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## 1.5.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/plugin-permission-react@0.4.22 + ## 1.5.4 ### Patch Changes diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index ac11707277..d5955d863e 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/app-defaults", - "version": "1.5.4", + "version": "1.5.5-next.1", "description": "Provides the default wiring of a Backstage App", "backstage": { "role": "web-library" diff --git a/packages/app-next-example-plugin/CHANGELOG.md b/packages/app-next-example-plugin/CHANGELOG.md index 6a10864bd5..fa7d0f3107 100644 --- a/packages/app-next-example-plugin/CHANGELOG.md +++ b/packages/app-next-example-plugin/CHANGELOG.md @@ -1,5 +1,21 @@ # app-next-example-plugin +## 0.0.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + +## 0.0.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/frontend-plugin-api@0.6.5-next.0 + ## 0.0.10 ### Patch Changes diff --git a/packages/app-next-example-plugin/package.json b/packages/app-next-example-plugin/package.json index dba0613d16..55dd01aea5 100644 --- a/packages/app-next-example-plugin/package.json +++ b/packages/app-next-example-plugin/package.json @@ -1,6 +1,6 @@ { "name": "app-next-example-plugin", - "version": "0.0.10", + "version": "0.0.11-next.1", "description": "Backstage internal example plugin", "backstage": { "role": "frontend-plugin" diff --git a/packages/app-next/.eslintrc.js b/packages/app-next/.eslintrc.js index e2a53a6ad2..6f6668b53d 100644 --- a/packages/app-next/.eslintrc.js +++ b/packages/app-next/.eslintrc.js @@ -1 +1,5 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { + rules: { + '@backstage/no-top-level-material-ui-4-imports': 'error', + }, +}); diff --git a/packages/app-next/CHANGELOG.md b/packages/app-next/CHANGELOG.md index 8b24a1e2d1..ab2c13c42a 100644 --- a/packages/app-next/CHANGELOG.md +++ b/packages/app-next/CHANGELOG.md @@ -1,5 +1,83 @@ # example-app-next +## 0.0.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.8.5-next.1 + - @backstage/plugin-scaffolder@1.19.4-next.1 + - @backstage/plugin-notifications@0.2.1-next.1 + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog@1.19.1-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/app-defaults@1.5.5-next.1 + - @backstage/cli@0.26.5-next.0 + - @backstage/frontend-app-api@0.6.5-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-api-docs@0.11.5-next.1 + - @backstage/plugin-app-visualizer@0.1.6-next.1 + - @backstage/plugin-auth-react@0.1.2-next.1 + - @backstage/plugin-catalog-graph@0.4.5-next.1 + - @backstage/plugin-catalog-import@0.10.11-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.2.4-next.1 + - @backstage/plugin-home@0.7.4-next.1 + - @backstage/plugin-kubernetes@0.11.10-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.11-next.1 + - @backstage/plugin-org@0.6.25-next.1 + - @backstage/plugin-search@1.4.11-next.1 + - @backstage/plugin-search-react@1.7.11-next.1 + - @backstage/plugin-signals@0.0.6-next.1 + - @backstage/plugin-techdocs@1.10.5-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.10-next.1 + - @backstage/plugin-techdocs-react@1.2.4-next.1 + - @backstage/plugin-user-settings@0.8.6-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## 0.0.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications@0.2.1-next.0 + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/plugin-api-docs@0.11.5-next.0 + - @backstage/plugin-catalog@1.19.1-next.0 + - @backstage/plugin-home@0.7.4-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-techdocs@1.10.5-next.0 + - @backstage/cli@0.26.5-next.0 + - @backstage/plugin-auth-react@0.1.1-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-scaffolder-react@1.8.5-next.0 + - @backstage/plugin-scaffolder@1.19.4-next.0 + - @backstage/plugin-catalog-graph@0.4.5-next.0 + - @backstage/plugin-catalog-import@0.10.11-next.0 + - @backstage/plugin-org@0.6.25-next.0 + - @backstage/plugin-search@1.4.11-next.0 + - @backstage/plugin-user-settings@0.8.6-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.4-next.0 + - @backstage/plugin-kubernetes@0.11.10-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.11-next.0 + - @backstage/plugin-techdocs-react@1.2.4-next.0 + - @backstage/app-defaults@1.5.5-next.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-app-api@0.6.5-next.0 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-app-visualizer@0.1.6-next.0 + - @backstage/plugin-permission-react@0.4.22 + - @backstage/plugin-search-common@1.2.11 + - @backstage/plugin-search-react@1.7.11-next.0 + - @backstage/plugin-signals@0.0.6-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.10-next.0 + ## 0.0.10 ### Patch Changes diff --git a/packages/app-next/README.md b/packages/app-next/README.md index 21645609a9..360e5d5545 100644 --- a/packages/app-next/README.md +++ b/packages/app-next/README.md @@ -1,3 +1,7 @@ # example-app-next -This package is an example of a Backstage application. +This package is an example of a Backstage application using the [new frontend](../../docs/frontend-system/index.md). + +To play with it, open a terminal and run the command: `yarn start` + +**NOTE:** Don't forget to open a second terminal and to launch the backend or [backend-next](../../docs/backend-system/index.md) there, using `yarn start`! The frontend requires a backend to connect to. diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index 8bfd79be54..271040ecec 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -10,7 +10,7 @@ app: extensions: # - apis.plugin.graphiql.browse.gitlab: true - - graphiql-endpoint:graphiql/gitlab: true + # - graphiql-endpoint:graphiql/gitlab: true # Entity page cards - entity-card:catalog/about @@ -18,7 +18,7 @@ app: - entity-card:catalog/links: config: filter: kind:component has:links - - entity-card:linguist/languages + # - entity-card:linguist/languages - entity-card:catalog-graph/relations: config: height: 300 diff --git a/packages/app-next/package.json b/packages/app-next/package.json index fdb84f3e81..d41ff02488 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -1,6 +1,6 @@ { "name": "example-app-next", - "version": "0.0.10", + "version": "0.0.11-next.1", "private": true, "repository": { "type": "git", diff --git a/packages/app-next/public/index.html b/packages/app-next/public/index.html index 1bbfda590d..63ab0bec0c 100644 --- a/packages/app-next/public/index.html +++ b/packages/app-next/public/index.html @@ -72,7 +72,10 @@ site: '<%= config.getOptionalString("app.datadogRum.site") || "datadoghq.com" %>', service: 'backstage', env: '<%= config.getString("app.datadogRum.env") %>', - sampleRate: 100, + sampleRate: + '<%= config.getOptionalNumber("app.datadogRum.sessionSampleRate") || 100 %>', + sessionReplaySampleRate: + '<%= config.getOptionalNumber("app.datadogRum.sessionReplaySampleRate") || 0 %>', trackInteractions: true, }); }); diff --git a/packages/app-next/src/examples/notFoundErrorPageExtension.tsx b/packages/app-next/src/examples/notFoundErrorPageExtension.tsx index f83671bc23..f5108c6c15 100644 --- a/packages/app-next/src/examples/notFoundErrorPageExtension.tsx +++ b/packages/app-next/src/examples/notFoundErrorPageExtension.tsx @@ -19,7 +19,8 @@ import { createComponentExtension, coreComponentRefs, } from '@backstage/frontend-plugin-api'; -import { Box, Typography } from '@material-ui/core'; +import Box from '@material-ui/core/Box'; +import Typography from '@material-ui/core/Typography'; import { Button } from '@backstage/core-components'; export function CustomNotFoundErrorPage() { diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 1d298af752..12e71ba6c0 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,79 @@ # example-app +## 0.2.97-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.8.5-next.1 + - @backstage/plugin-scaffolder@1.19.4-next.1 + - @backstage/plugin-notifications@0.2.1-next.1 + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog@1.19.1-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/app-defaults@1.5.5-next.1 + - @backstage/cli@0.26.5-next.0 + - @backstage/frontend-app-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-api-docs@0.11.5-next.1 + - @backstage/plugin-auth-react@0.1.2-next.1 + - @backstage/plugin-catalog-graph@0.4.5-next.1 + - @backstage/plugin-catalog-import@0.10.11-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.2.4-next.1 + - @backstage/plugin-devtools@0.1.14-next.1 + - @backstage/plugin-home@0.7.4-next.1 + - @backstage/plugin-kubernetes@0.11.10-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.11-next.1 + - @backstage/plugin-org@0.6.25-next.1 + - @backstage/plugin-search@1.4.11-next.1 + - @backstage/plugin-search-react@1.7.11-next.1 + - @backstage/plugin-signals@0.0.6-next.1 + - @backstage/plugin-techdocs@1.10.5-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.10-next.1 + - @backstage/plugin-techdocs-react@1.2.4-next.1 + - @backstage/plugin-user-settings@0.8.6-next.1 + +## 0.2.97-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications@0.2.1-next.0 + - @backstage/plugin-api-docs@0.11.5-next.0 + - @backstage/plugin-catalog@1.19.1-next.0 + - @backstage/plugin-home@0.7.4-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-techdocs@1.10.5-next.0 + - @backstage/cli@0.26.5-next.0 + - @backstage/plugin-auth-react@0.1.1-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-scaffolder-react@1.8.5-next.0 + - @backstage/plugin-scaffolder@1.19.4-next.0 + - @backstage/plugin-catalog-graph@0.4.5-next.0 + - @backstage/plugin-catalog-import@0.10.11-next.0 + - @backstage/plugin-devtools@0.1.14-next.0 + - @backstage/plugin-org@0.6.25-next.0 + - @backstage/plugin-search@1.4.11-next.0 + - @backstage/plugin-user-settings@0.8.6-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.4-next.0 + - @backstage/plugin-kubernetes@0.11.10-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.11-next.0 + - @backstage/plugin-techdocs-react@1.2.4-next.0 + - @backstage/app-defaults@1.5.5-next.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-app-api@0.6.5-next.0 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-permission-react@0.4.22 + - @backstage/plugin-search-common@1.2.11 + - @backstage/plugin-search-react@1.7.11-next.0 + - @backstage/plugin-signals@0.0.6-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.10-next.0 + ## 0.2.96 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 809bf35313..20d0d0312f 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "example-app", - "version": "0.2.96", + "version": "0.2.97-next.1", "backstage": { "role": "frontend" }, diff --git a/packages/app/public/index.html b/packages/app/public/index.html index 631666da28..9a8267e98e 100644 --- a/packages/app/public/index.html +++ b/packages/app/public/index.html @@ -72,7 +72,10 @@ site: '<%= config.getOptionalString("app.datadogRum.site") || "datadoghq.com" %>', service: 'backstage', env: '<%= config.getString("app.datadogRum.env") %>', - sampleRate: 100, + sampleRate: + '<%= config.getOptionalNumber("app.datadogRum.sessionSampleRate") || 100 %>', + sessionReplaySampleRate: + '<%= config.getOptionalNumber("app.datadogRum.sessionReplaySampleRate") || 0 %>', trackInteractions: true, }); }); diff --git a/packages/backend-app-api/CHANGELOG.md b/packages/backend-app-api/CHANGELOG.md index 15d50cac23..733a19d81e 100644 --- a/packages/backend-app-api/CHANGELOG.md +++ b/packages/backend-app-api/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/backend-app-api +## 0.7.2-next.1 + +### Patch Changes + +- 09f8988: Remove explicit `alg` check for user tokens in `verifyToken` +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/cli-node@0.2.5 + - @backstage/config-loader@1.8.0 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.7.1-next.0 + +### Patch Changes + +- 4cd5ff0: Add ability to configure the Node.js HTTP Server when configuring the root HTTP Router service +- e8199b1: Move the JWKS registration outside of the lifecycle middleware +- dc8c5dd: The default `TokenManager` implementation no longer requires keys to be configured in production, but it will throw an errors when generating or authenticating tokens. The default `AuthService` implementation will now also provide additional context if such an error is throw when falling back to using the `TokenManager` service to generate tokens for outgoing requests. +- 025641b: Redact `meta` fields too with the logger +- 5863e02: Internal refactor to only create one external token handler +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.5 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.8.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-node@0.7.29-next.0 + ## 0.7.0 ### Minor Changes diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index 072efa7de8..bb53e78ff1 100644 --- a/packages/backend-app-api/package.json +++ b/packages/backend-app-api/package.json @@ -1,20 +1,30 @@ { "name": "@backstage/backend-app-api", + "version": "0.7.2-next.1", "description": "Core API used by Backstage backend apps", - "version": "0.7.0", - "main": "src/index.ts", - "types": "src/index.ts", - "publishConfig": { - "access": "public" - }, "backstage": { "role": "node-library" }, + "publishConfig": { + "access": "public" + }, + "keywords": [ + "backstage" + ], + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/backend-app-api" + }, + "license": "Apache-2.0", "exports": { ".": "./src/index.ts", "./alpha": "./src/alpha.ts", "./package.json": "./package.json" }, + "main": "src/index.ts", + "types": "src/index.ts", "typesVersions": { "*": { "alpha": [ @@ -25,16 +35,11 @@ ] } }, - "homepage": "https://backstage.io", - "repository": { - "type": "git", - "url": "https://github.com/backstage/backstage", - "directory": "packages/backend-app-api" - }, - "keywords": [ - "backstage" + "files": [ + "dist", + "config.d.ts", + "migrations/**/*.{js,d.ts}" ], - "license": "Apache-2.0", "scripts": { "build": "backstage-cli package build", "clean": "backstage-cli package clean", @@ -96,11 +101,5 @@ "msw": "^1.0.0", "supertest": "^6.1.3" }, - "configSchema": "config.d.ts", - "files": [ - "dist", - "config.d.ts", - "alpha", - "migrations/**/*.{js,d.ts}" - ] + "configSchema": "config.d.ts" } diff --git a/packages/backend-app-api/src/logging/WinstonLogger.test.ts b/packages/backend-app-api/src/logging/WinstonLogger.test.ts index c7173b1cbe..d025719d9a 100644 --- a/packages/backend-app-api/src/logging/WinstonLogger.test.ts +++ b/packages/backend-app-api/src/logging/WinstonLogger.test.ts @@ -22,6 +22,17 @@ function msg(info: TransformableInfo): TransformableInfo { } describe('WinstonLogger', () => { + it('creates a winston logger instance with default options', () => { + const logger = WinstonLogger.create({}); + expect(logger).toBeInstanceOf(WinstonLogger); + }); + + it('creates a child logger', () => { + const logger = WinstonLogger.create({}); + const childLogger = logger.child({ plugin: 'test-plugin' }); + expect(childLogger).toBeInstanceOf(WinstonLogger); + }); + it('redacter should redact and escape regex', () => { const redacter = WinstonLogger.redacter(); const log = { @@ -47,4 +58,24 @@ describe('WinstonLogger', () => { }), ); }); + + it('redacter should redact nested object', () => { + const redacter = WinstonLogger.redacter(); + const log = { + level: 'error', + message: { + nested: 'hello (world) from nested object', + }, + }; + + redacter.add(['hello']); + expect(redacter.format.transform(msg(log))).toEqual( + msg({ + ...log, + message: { + nested: '[REDACTED] (world) from nested object', + }, + }), + ); + }); }); diff --git a/packages/backend-app-api/src/logging/WinstonLogger.ts b/packages/backend-app-api/src/logging/WinstonLogger.ts index 8a3eb28100..8d4b37bb8d 100644 --- a/packages/backend-app-api/src/logging/WinstonLogger.ts +++ b/packages/backend-app-api/src/logging/WinstonLogger.ts @@ -85,16 +85,20 @@ export class WinstonLogger implements RootLoggerService { let redactionPattern: RegExp | undefined = undefined; + const replace = (obj: TransformableInfo) => { + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + if (typeof obj[key] === 'object') { + obj[key] = replace(obj[key] as TransformableInfo); + } else if (typeof obj[key] === 'string') { + obj[key] = obj[key]?.replace(redactionPattern, '[REDACTED]'); + } + } + } + return obj; + }; return { - format: format(info => { - if (redactionPattern && typeof info.message === 'string') { - info.message = info.message.replace(redactionPattern, '[REDACTED]'); - } - if (redactionPattern && typeof info.stack === 'string') { - info.stack = info.stack.replace(redactionPattern, '[REDACTED]'); - } - return info; - })(), + format: format(replace)(), add(newRedactions) { let added = 0; for (const redactionToTrim of newRedactions) { diff --git a/packages/backend-app-api/src/services/implementations/auth/user/UserTokenHandler.test.ts b/packages/backend-app-api/src/services/implementations/auth/user/UserTokenHandler.test.ts index f2a86e4a20..b0e8418655 100644 --- a/packages/backend-app-api/src/services/implementations/auth/user/UserTokenHandler.test.ts +++ b/packages/backend-app-api/src/services/implementations/auth/user/UserTokenHandler.test.ts @@ -133,6 +133,34 @@ describe('UserTokenHandler', () => { ).rejects.toThrow('signature verification failed'); }); + it('should fail to verify tokens that have a bad alg', async () => { + const expectedIssuedAt = 1712071714; + const expectedExpiresAt = 1712075314; + + jest.useFakeTimers({ + now: expectedIssuedAt * 1000 + 600_000, + }); + + const header = encodeData({ + typ: 'vnd.backstage.user', + alg: 'none', + }); + const payload = encodeData({ + iss: 'http://localhost:7007/api/auth', + sub: 'user:development/guest', + ent: ['user:development/guest', 'group:default/team-a'], + aud: 'backstage', + iat: expectedIssuedAt, + exp: expectedExpiresAt, + uip: 'proof', + }); + const token = `${header}.${payload}.`; + + await expect(userTokenHandler.verifyToken(token)).rejects.toThrow( + /Unsupported "alg" value/, + ); + }); + it('should verify a valid legacy backstage token', async () => { const expectedIssuedAt = 1712071714; const expectedExpiresAt = 1712075314; @@ -151,7 +179,7 @@ describe('UserTokenHandler', () => { sub: 'user:development/guest', ent: ['user:development/guest', 'group:default/team-a'], aud: 'backstage', - iat: 1712071714, + iat: expectedIssuedAt, exp: expectedExpiresAt, }, }; @@ -179,7 +207,7 @@ describe('UserTokenHandler', () => { iss: 'http://localhost:7007/api/auth', ent: ['user:development/guest', 'group:default/team-a'], aud: 'backstage', - iat: 1712071714, + iat: expectedIssuedAt, exp: expectedExpiresAt, }, }; @@ -209,7 +237,7 @@ describe('UserTokenHandler', () => { sub: 'user:development/guest', ent: ['user:development/guest', 'group:default/team-a'], aud: 'backstage', - iat: 1712071714, + iat: expectedIssuedAt, exp: expectedExpiresAt, uip: 'proof', }, @@ -239,7 +267,7 @@ describe('UserTokenHandler', () => { payload: { sub: 'user:development/guest', ent: ['user:development/guest', 'group:default/team-a'], - iat: 1712071714, + iat: expectedIssuedAt, exp: expectedExpiresAt, }, }; diff --git a/packages/backend-app-api/src/services/implementations/auth/user/UserTokenHandler.ts b/packages/backend-app-api/src/services/implementations/auth/user/UserTokenHandler.ts index af3faae22d..bd73d8701f 100644 --- a/packages/backend-app-api/src/services/implementations/auth/user/UserTokenHandler.ts +++ b/packages/backend-app-api/src/services/implementations/auth/user/UserTokenHandler.ts @@ -34,18 +34,14 @@ import { JwksClient } from '../JwksClient'; */ export class UserTokenHandler { static create(options: { discovery: DiscoveryService }): UserTokenHandler { - const algorithms = ['ES256']; // TODO: configurable? const jwksClient = new JwksClient(async () => { const url = await options.discovery.getBaseUrl('auth'); return new URL(`${url}/.well-known/jwks.json`); }); - return new UserTokenHandler(algorithms, jwksClient); + return new UserTokenHandler(jwksClient); } - constructor( - private readonly algorithms: string[], - private readonly jwksClient: JwksClient, - ) {} + constructor(private readonly jwksClient: JwksClient) {} async verifyToken(token: string) { const verifyOpts = this.#getTokenVerificationOptions(token); @@ -79,7 +75,6 @@ export class UserTokenHandler { if (typ === tokenTypes.user.typParam) { return { - algorithms: this.algorithms, requiredClaims: ['iat', 'exp', 'sub'], typ: tokenTypes.user.typParam, }; @@ -87,7 +82,6 @@ export class UserTokenHandler { if (typ === tokenTypes.limitedUser.typParam) { return { - algorithms: this.algorithms, requiredClaims: ['iat', 'exp', 'sub'], typ: tokenTypes.limitedUser.typParam, }; @@ -96,7 +90,6 @@ export class UserTokenHandler { const { aud } = decodeJwt(token); if (aud === tokenTypes.user.audClaim) { return { - algorithms: this.algorithms, audience: tokenTypes.user.audClaim, }; } diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index 61b95f211f..c8e459ba78 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,39 @@ # @backstage/backend-common +## 0.22.0-next.1 + +### Minor Changes + +- ed83f85: Internal refactor of the database code. + + **BREAKING**: The helper functions `createDatabaseClient` and `ensureDatabaseExists` have been removed from the public interface, since they have no usage within the repository and never were suitable for calling from the outside. Please consider using `coreServices.database` or `DatabaseManager` directly wherever possible instead. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.7.2-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/config-loader@1.8.0 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.21.8-next.0 + +### Patch Changes + +- ba0b8b4: Added option to `ServerTokenManager.fromConfig` that allows it to be instantiated in production without any configured keys. +- Updated dependencies + - @backstage/backend-app-api@0.7.1-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/backend-dev-utils@0.1.4 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.8.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + ## 0.21.7 ### Patch Changes diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index f9e0d76aab..cccf326b9f 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -30,12 +30,11 @@ import { GiteaIntegration } from '@backstage/integration'; import { GithubCredentialsProvider } from '@backstage/integration'; import { GithubIntegration } from '@backstage/integration'; import { GitLabIntegration } from '@backstage/integration'; +import { HarnessIntegration } from '@backstage/integration'; import { HostDiscovery as HostDiscovery_2 } from '@backstage/backend-app-api'; import { HttpAuthService } from '@backstage/backend-plugin-api'; import { IdentityService } from '@backstage/backend-plugin-api'; import { isChildPath } from '@backstage/cli-common'; -import { Knex } from 'knex'; -import knexFactory from 'knex'; import { KubeConfig } from '@kubernetes/client-node'; import { LifecycleService } from '@backstage/backend-plugin-api'; import { LoadConfigOptionsRemote } from '@backstage/config-loader'; @@ -225,16 +224,6 @@ export interface ContainerRunner { runContainer(opts: RunContainerOptions): Promise<void>; } -// @public -export function createDatabaseClient( - dbConfig: Config, - overrides?: Partial<Knex.Config>, - deps?: { - lifecycle: LifecycleService; - pluginMetadata: PluginMetadataService; - }, -): knexFactory.Knex<any, any[]>; - // @public export function createLegacyAuthAdapters< TOptions extends { @@ -315,13 +304,7 @@ export class DockerContainerRunner implements ContainerRunner { // @public export function dropDatabase( dbConfig: Config, - ...databases: Array<string> -): Promise<void>; - -// @public -export function ensureDatabaseExists( - dbConfig: Config, - ...databases: Array<string> + ...databaseNames: string[] ): Promise<void>; // @public @@ -530,6 +513,23 @@ export class GitlabUrlReader implements UrlReader { toString(): string; } +// @public +export class HarnessUrlReader implements UrlReader { + constructor(integration: HarnessIntegration); + // (undocumented) + static factory: ReaderFactory; + // (undocumented) + read(url: string): Promise<Buffer>; + // (undocumented) + readTree(): Promise<ReadTreeResponse>; + // (undocumented) + readUrl(url: string, options?: ReadUrlOptions): Promise<ReadUrlResponse>; + // (undocumented) + search(): Promise<SearchResponse>; + // (undocumented) + toString(): string; +} + // @public export const HostDiscovery: typeof HostDiscovery_2; diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index 700d9a6c6c..c6263cde97 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -111,6 +111,13 @@ export interface Config { * Defaults to true if unspecified. */ ensureExists?: boolean; + /** + * Whether to ensure the given database schema exists by creating it if it does not. + * Defaults to false if unspecified. + * + * NOTE: Currently only supported by the `pg` client when pluginDivisionMode: schema + */ + ensureSchemaExists?: boolean; /** * How plugins databases are managed/divided in the provided database instance. * @@ -147,6 +154,13 @@ export interface Config { * Defaults to base config if unspecified. */ ensureExists?: boolean; + /** + * Whether to ensure the given database schema exists by creating it if it does not. + * Defaults to false if unspecified. + * + * NOTE: Currently only supported by the `pg` client when pluginDivisionMode: schema + */ + ensureSchemaExists?: boolean; /** * Arbitrary config object to pass to knex when initializing * (https://knexjs.org/#Installation-client). Most notable is the diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 9897afcf5a..42c4c49863 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,18 +1,31 @@ { "name": "@backstage/backend-common", + "version": "0.22.0-next.1", "description": "Common functionality library for Backstage backends", - "version": "0.21.7", - "main": "src/index.ts", - "types": "src/index.ts", + "backstage": { + "role": "node-library" + }, "publishConfig": { "access": "public" }, + "keywords": [ + "backstage" + ], + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/backend-common" + }, + "license": "Apache-2.0", "exports": { ".": "./src/index.ts", "./alpha": "./src/alpha.ts", "./testUtils": "./src/testUtils.ts", "./package.json": "./package.json" }, + "main": "src/index.ts", + "types": "src/index.ts", "typesVersions": { "*": { "alpha": [ @@ -26,27 +39,18 @@ ] } }, - "backstage": { - "role": "node-library" - }, - "homepage": "https://backstage.io", - "repository": { - "type": "git", - "url": "https://github.com/backstage/backstage", - "directory": "packages/backend-common" - }, - "keywords": [ - "backstage" + "files": [ + "dist", + "config.d.ts" ], - "license": "Apache-2.0", "scripts": { "build": "backstage-cli package build", + "clean": "backstage-cli package clean", "lint": "backstage-cli package lint", - "test": "backstage-cli package test", "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", - "clean": "backstage-cli package clean", "start": "backstage-cli package start", + "test": "backstage-cli package test", "test:kubernetes": "backstage-cli package test -t KubernetesContainerRunner --no-watch" }, "dependencies": { @@ -108,14 +112,6 @@ "yauzl": "^3.0.0", "yn": "^4.0.0" }, - "peerDependencies": { - "pg-connection-string": "^2.3.0" - }, - "peerDependenciesMeta": { - "pg-connection-string": { - "optional": true - } - }, "devDependencies": { "@aws-sdk/util-stream-node": "^3.350.0", "@backstage/backend-test-utils": "workspace:^", @@ -138,10 +134,13 @@ "mysql2": "^3.0.0", "supertest": "^6.1.3" }, - "files": [ - "dist", - "config.d.ts", - "alpha" - ], + "peerDependencies": { + "pg-connection-string": "^2.3.0" + }, + "peerDependenciesMeta": { + "pg-connection-string": { + "optional": true + } + }, "configSchema": "config.d.ts" } diff --git a/packages/backend-common/src/database/DatabaseManager.test.ts b/packages/backend-common/src/database/DatabaseManager.test.ts index bf4af17d05..347ec742c0 100644 --- a/packages/backend-common/src/database/DatabaseManager.test.ts +++ b/packages/backend-common/src/database/DatabaseManager.test.ts @@ -13,839 +13,111 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { ConfigReader } from '@backstage/config'; -import { omit } from 'lodash'; -import path from 'path'; -import { - createDatabaseClient, - ensureDatabaseExists, - ensureSchemaExists, -} from './connection'; -import { DatabaseManager } from './DatabaseManager'; +import { DatabaseManagerImpl } from './DatabaseManager'; +import { Connector } from './types'; -jest.mock('./connection', () => ({ - ...jest.requireActual('./connection'), - createDatabaseClient: jest.fn(), - ensureDatabaseExists: jest.fn(), - ensureSchemaExists: jest.fn(), -})); - -describe('DatabaseManager', () => { - // This is similar to the ts-jest `mocked` helper. - const mocked = (f: Function) => f as jest.Mock; - - afterEach(() => jest.resetAllMocks()); - - describe('DatabaseManager.fromConfig', () => { - const backendConfig = { - backend: { - database: { - client: 'pg', - connection: { - host: 'localhost', - user: 'foo', - password: 'bar', - database: 'foodb', - }, - }, - }, - }; - - it('accesses the backend.database key', () => { - const config = new ConfigReader(backendConfig); - const getConfigSpy = jest.spyOn(config, 'getConfig'); - DatabaseManager.fromConfig(config); - - expect(getConfigSpy).toHaveBeenCalledWith('backend.database'); - }); - - it('handles default options', () => { - const config = new ConfigReader(backendConfig); - const database = DatabaseManager.fromConfig(config); - const client = database.forPlugin('test'); - - expect(client.migrations?.skip).toBe(false); - }); - - it('handles migrations options', () => { - const config = new ConfigReader(backendConfig); - const database = DatabaseManager.fromConfig(config, { - migrations: { skip: true }, - }); - const client = database.forPlugin('test'); - - expect(client.migrations?.skip).toBe(true); - }); +describe('DatabaseManagerImpl', () => { + afterEach(() => { + jest.clearAllMocks(); }); - describe('DatabaseManager.forPlugin', () => { - const config = { - backend: { - database: { - client: 'pg', - prefix: 'test_prefix_', - connection: { - host: 'localhost', - user: 'foo', - password: 'bar', - database: 'foodb', - }, - plugin: { - testdbname: { - connection: { - database: 'database_name_overridden', - }, - }, - differentclient: { - client: 'better-sqlite3', - connection: { - filename: 'plugin_with_different_client', - }, - }, - differentclientconnstring: { - client: 'better-sqlite3', - connection: ':memory:', - }, - stringoverride: { - connection: 'postgresql://testuser:testpass@acme:5432/userdbname', - }, - }, - }, + it('calls the right connector, only once per plugin id', async () => { + const connector1 = { + getClient: jest.fn(), + dropDatabase: jest.fn(), + } satisfies Connector; + const connector2 = { + getClient: jest.fn(), + dropDatabase: jest.fn(), + } satisfies Connector; + + const impl = new DatabaseManagerImpl( + new ConfigReader({ + client: 'pg', + }), + { + pg: connector1, + notpg: connector2, }, - }; - let manager: DatabaseManager; + ); - beforeEach(() => { - manager = DatabaseManager.fromConfig(new ConfigReader(config)); - }); + await impl.forPlugin('plugin1').getClient(); + expect(connector1.getClient).toHaveBeenCalledTimes(1); + expect(connector1.getClient).toHaveBeenLastCalledWith('plugin1', undefined); + expect(connector2.getClient).toHaveBeenCalledTimes(0); - it('connects to a plugin database using default config', async () => { - const pluginId = 'pluginwithoutconfig'; + await impl.forPlugin('plugin1').getClient(); + expect(connector1.getClient).toHaveBeenCalledTimes(1); + expect(connector1.getClient).toHaveBeenLastCalledWith('plugin1', undefined); + expect(connector2.getClient).toHaveBeenCalledTimes(0); - await manager.forPlugin(pluginId).getClient(); - expect(mocked(createDatabaseClient)).toHaveBeenCalledTimes(1); + await impl.forPlugin('plugin2').getClient(); + expect(connector1.getClient).toHaveBeenCalledTimes(2); + expect(connector1.getClient).toHaveBeenLastCalledWith('plugin2', undefined); + expect(connector2.getClient).toHaveBeenCalledTimes(0); + }); - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig, overrides] = mockCalls[0]; + it('respects per-plugin overridden connectors', async () => { + const connector1 = { + getClient: jest.fn(), + dropDatabase: jest.fn(), + } satisfies Connector; + const connector2 = { + getClient: jest.fn(), + dropDatabase: jest.fn(), + } satisfies Connector; - // default config should be passed through to underlying connector - expect(baseConfig.get()).toMatchObject({ + const impl = new DatabaseManagerImpl( + new ConfigReader({ client: 'pg', - connection: omit(config.backend.database.connection, ['database']), - }); - - // override using database name generated from pluginId and prefix - expect(overrides).toMatchObject({ - connection: { - database: `${config.backend.database.prefix}${pluginId}`, - }, - }); - }); - - it('provides a plugin db which uses components from top level connection string', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'pg', - connection: 'postgresql://foo:bar@acme:5432/foodb', - }, - }, - }), - ); - - await testManager.forPlugin('pluginwithoutconfig').getClient(); - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig, overrides] = mockCalls[0]; - - // parsed connection string **without** db name should be passed through - expect(baseConfig.get()).toMatchObject({ - connection: { - host: 'acme', - user: 'foo', - password: 'bar', - port: '5432', - application_name: 'backstage_plugin_pluginwithoutconfig', - }, - }); - - // we expect a pg database name override with ${prefix} followed by pluginId - expect(overrides).toHaveProperty( - 'connection.database', - expect.stringContaining('pluginwithoutconfig'), - ); - }); - - it('provides an inmemory sqlite database if top level is also inmemory and plugin config is not present', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'better-sqlite3', - connection: ':memory:', - }, - }, - }), - ); - - await testManager.forPlugin('pluginwithoutconfig').getClient(); - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [_, overrides] = mockCalls[0]; - - expect(overrides).toHaveProperty( - 'connection.filename', - expect.stringContaining(':memory:'), - ); - }); - - it('throws if top level sqlite filename is provided', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'better-sqlite3', - connection: 'some-file-path', - }, - }, - }), - ); - - await expect( - testManager.forPlugin('pluginwithoutconfig').getClient(), - ).rejects.toBeInstanceOf(Error); - }); - - it('creates plugin-specific sqlite files when plugin config is not present', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'better-sqlite3', - connection: { - directory: 'sqlite-files', - }, - }, - }, - }), - ); - - await testManager.forPlugin('pluginwithoutconfig').getClient(); - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [_, overrides] = mockCalls[0]; - - expect(overrides).toHaveProperty( - 'connection.filename', - path.join('sqlite-files', 'pluginwithoutconfig.sqlite'), - ); - }); - - it('uses sqlite directory from top level config and filename from plugin config', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'better-sqlite3', - connection: { - directory: 'sqlite-files', - }, - plugin: { - test: { - connection: { - filename: 'other.sqlite', - }, - }, - }, - }, - }, - }), - ); - - await testManager.forPlugin('test').getClient(); - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [_, overrides] = mockCalls[0]; - - expect(overrides).toHaveProperty( - 'connection.filename', - path.join('sqlite-files', 'other.sqlite'), - ); - }); - - it('uses sqlite directory and filename from plugin config', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'better-sqlite3', - connection: { - directory: 'sqlite-files', - }, - plugin: { - test: { - connection: { - directory: 'custom-sqlite-files', - filename: 'other.sqlite', - }, - }, - }, - }, - }, - }), - ); - - await testManager.forPlugin('test').getClient(); - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [_, overrides] = mockCalls[0]; - - expect(overrides).toHaveProperty( - 'connection.filename', - path.join('custom-sqlite-files', 'other.sqlite'), - ); - }); - - it('connects to a plugin database using a specific database name', async () => { - // testdbname.connection.database is set in config - await manager.forPlugin('testdbname').getClient(); - - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [_baseConfig, overrides] = mockCalls[0]; - - // simple case where only database name is overridden - expect(overrides).toMatchObject({ - connection: { - database: 'database_name_overridden', - }, - }); - }); - - it('ensure plugin specific database is created', async () => { - const pluginId = 'testdbname'; - // testdbname.connection.database is set in config - await manager.forPlugin(pluginId).getClient(); - - const mockCalls = mocked(ensureDatabaseExists).mock.calls.splice(-1); - const [_, dbname] = mockCalls[0]; - - expect(dbname).toEqual( - config.backend.database.plugin[pluginId].connection.database, - ); - }); - - it('provides different plugins with their own databases', async () => { - await manager.forPlugin('plugin1').getClient(); - await manager.forPlugin('plugin2').getClient(); - - expect(mocked(createDatabaseClient)).toHaveBeenCalledTimes(2); - - const mockCalls = mocked(createDatabaseClient).mock.calls; - const [plugin1CallArgs, plugin2CallArgs] = mockCalls; - - // database name overrides should be different - expect(plugin1CallArgs[1].connection.database).not.toEqual( - plugin2CallArgs[1].connection.database, - ); - }); - - it('returns the same client for the same pluginId', async () => { - const [client1, client2] = await Promise.all([ - manager.forPlugin('plugin1').getClient(), - manager.forPlugin('plugin1').getClient(), - ]); - expect(mocked(createDatabaseClient)).toHaveBeenCalledTimes(1); - - expect(client1).toBe(client2); - }); - - it('uses plugin connection as base if default client is different from plugin client', async () => { - const pluginId = 'differentclient'; - await manager.forPlugin(pluginId).getClient(); - - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig, _overrides] = mockCalls[0]; - - // plugin connection should be used as base config, client is different - expect(baseConfig.get()).toMatchObject({ - client: 'better-sqlite3', - connection: config.backend.database.plugin[pluginId].connection, - }); - }); - - it('provides database client specific base and override when client set under plugin', async () => { - const pluginId = 'differentclient'; - await manager.forPlugin(pluginId).getClient(); - - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig, overrides] = mockCalls[0]; - - // plugin client should be better-sqlite3 - expect(baseConfig.get().client).toEqual('better-sqlite3'); - - // SQLite uses 'filename' instead of 'database' - expect(overrides).toHaveProperty( - 'connection.filename', - 'plugin_with_different_client', - ); - }); - - it('provides database client specific base from plugin connection string when client set under plugin', async () => { - const pluginId = 'differentclientconnstring'; - await manager.forPlugin(pluginId).getClient(); - - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig, overrides] = mockCalls[0]; - - expect(baseConfig.get().client).toEqual('better-sqlite3'); - - expect(overrides).toHaveProperty('connection.filename', ':memory:'); - }); - - it('generates a database name override when prefix is not explicitly set', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'pg', - connection: { - host: 'localhost', - user: 'foo', - password: 'bar', - database: 'foodb', - }, - }, - }, - }), - ); - - await testManager.forPlugin('testplugin').getClient(); - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [_baseConfig, overrides] = mockCalls[0]; - - expect(overrides).toHaveProperty( - 'connection.database', - expect.stringContaining('backstage_plugin_'), - ); - }); - - it('generates a database name override when prefix is not explicitly set for mysql', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'mysql', - connection: { - host: 'localhost', - user: 'foo', - password: 'bar', - database: 'foodb', - }, - }, - }, - }), - ); - - await testManager.forPlugin('testplugin').getClient(); - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [_baseConfig, overrides] = mockCalls[0]; - - expect(overrides).toHaveProperty( - 'connection.database', - expect.stringContaining('backstage_plugin_'), - ); - }); - - it('uses values from plugin connection string if top level client should be used', async () => { - const pluginId = 'stringoverride'; - await manager.forPlugin(pluginId).getClient(); - - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig, overrides] = mockCalls[0]; - - // plugin client should be pg - expect(baseConfig.get().client).toEqual('pg'); - - expect(overrides).toHaveProperty( - 'connection.database', - expect.stringContaining('userdbname'), - ); - }); - - it('plugin sets schema override for pg client', async () => { - const overrideConfig = { - backend: { - database: { - client: 'pg', - pluginDivisionMode: 'schema', - connection: { - host: 'localhost', - user: 'foo', - password: 'bar', - database: 'foodb', - }, + plugin: { + plugin2: { + client: 'mysql', }, }, - }; - const testManager = DatabaseManager.fromConfig( - new ConfigReader(overrideConfig), - ); - const pluginId = 'schemaoverride'; - await testManager.forPlugin(pluginId).getClient(); + }), + { + pg: connector1, + mysql: connector2, + }, + ); - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig, overrides] = mockCalls[0]; + await impl.forPlugin('plugin1').getClient(); + expect(connector1.getClient).toHaveBeenCalledTimes(1); + expect(connector1.getClient).toHaveBeenLastCalledWith('plugin1', undefined); + expect(connector2.getClient).toHaveBeenCalledTimes(0); - expect(baseConfig.get()).toMatchObject({ - client: 'pg', - connection: config.backend.database.connection, - }); + await impl.forPlugin('plugin2').getClient(); + expect(connector1.getClient).toHaveBeenCalledTimes(1); + expect(connector1.getClient).toHaveBeenLastCalledWith('plugin1', undefined); + expect(connector2.getClient).toHaveBeenCalledTimes(1); + expect(connector2.getClient).toHaveBeenLastCalledWith('plugin2', undefined); + }); - expect(overrides).toMatchObject({ - searchPath: [pluginId], - }); + it('retains the migration skip info', async () => { + const connector = { + getClient: jest.fn(), + dropDatabase: jest.fn(), + } satisfies Connector; + + const impl1 = new DatabaseManagerImpl(new ConfigReader({ client: 'pg' }), { + pg: connector, }); - it('plugin does not provide schema override for non pg client', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'better-sqlite3', - pluginDivisionMode: 'schema', - connection: { - host: 'localhost', - user: 'foo', - password: 'bar', - database: 'foodb', - }, - }, - }, - }), - ); - const pluginId = 'any-plugin'; - await testManager.forPlugin(pluginId).getClient(); + const impl2 = new DatabaseManagerImpl( + new ConfigReader({ client: 'pg' }), + { pg: connector }, + { migrations: { skip: true } }, + ); - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig, overrides] = mockCalls[0]; - - expect(baseConfig.get()).toMatchObject({ - client: 'better-sqlite3', - connection: config.backend.database.connection, - }); - - expect(overrides).not.toHaveProperty('searchPath'); + expect((await impl1.forPlugin('plugin1')).migrations).toEqual({ + skip: false, }); - it('plugin does not provide schema override if pluginDivisionMode is set to database', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'pg', - pluginDivisionMode: 'database', - connection: 'some-file-path', - }, - }, - }), - ); - - const pluginId = 'any-plugin'; - await testManager.forPlugin(pluginId).getClient(); - - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [_baseConfig, overrides] = mockCalls[0]; - - expect(overrides).not.toHaveProperty('searchPath'); - }); - - it('plugin does not provide schema override if pluginDivisionMode is not set', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'pg', - connection: { - host: 'localhost', - user: 'foo', - password: 'bar', - database: 'foodb', - }, - }, - }, - }), - ); - - const pluginId = 'schemaoverride'; - await testManager.forPlugin(pluginId).getClient(); - - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [_baseConfig, overrides] = mockCalls[0]; - - expect(overrides).not.toHaveProperty('searchPath'); - }); - - it('pluginDivisionMode ensures that each plugin schema exists', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'pg', - pluginDivisionMode: 'schema', - connection: { - host: 'localhost', - user: 'foo', - password: 'bar', - database: 'foodb', - }, - }, - }, - }), - ); - const pluginId = 'testdbname'; - await testManager.forPlugin(pluginId).getClient(); - - const mockCalls = mocked(ensureSchemaExists).mock.calls; - const [_, schemaName] = mockCalls[0]; - - expect(schemaName).toEqual('testdbname'); - }); - - it('pluginDivisionMode allows connection overrides for plugins', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'pg', - pluginDivisionMode: 'schema', - connection: { - host: 'localhost', - user: 'foo', - password: 'bar', - database: 'foodb', - }, - plugin: { - testdbname: { - connection: { - database: 'database_name_overridden', - host: 'newhost', - }, - }, - }, - }, - }, - }), - ); - const pluginId = 'testdbname'; - await testManager.forPlugin(pluginId).getClient(); - - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig, overrides] = mockCalls[0]; - - expect(baseConfig.get()).toMatchObject({ - client: 'pg', - connection: { - database: 'database_name_overridden', - host: 'newhost', - user: 'foo', - password: 'bar', - }, - }); - expect(overrides).toHaveProperty('searchPath', ['testdbname']); - expect(overrides).toHaveProperty( - 'connection.database', - 'database_name_overridden', - ); - }); - - it('ensureExists does not create database or schema when false', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'pg', - pluginDivisionMode: 'schema', - ensureExists: false, - connection: { - host: 'localhost', - user: 'foo', - password: 'bar', - database: 'foodb', - }, - }, - }, - }), - ); - const pluginId = 'testdbname'; - await testManager.forPlugin(pluginId).getClient(); - - expect(mocked(ensureDatabaseExists)).toHaveBeenCalledTimes(0); - expect(mocked(ensureSchemaExists)).toHaveBeenCalledTimes(0); - }); - - it('fetches and merges additional knex config', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'pg', - connection: { - host: 'localhost', - database: 'foodb', - }, - knexConfig: { - something: false, - }, - plugin: { - testdbname: { - knexConfig: { - debug: true, - }, - }, - }, - }, - }, - }), - ); - await testManager.forPlugin('testdbname').getClient(); - - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig] = mockCalls[0]; - - expect(baseConfig.data).toEqual( - expect.objectContaining({ - debug: true, - something: false, - }), - ); - }); - - it('sets the owner config for plugin using default config', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'pg', - connection: { - host: 'localhost', - database: 'foodb', - }, - role: 'backstage', - plugin: { - testowner: {}, - }, - }, - }, - }), - ); - await testManager.forPlugin('testowner').getClient(); - - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig] = mockCalls[0]; - - expect(baseConfig.data.role).toEqual('backstage'); - }); - - it('sets the owner config for plugin using plugin config', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'pg', - connection: { - host: 'localhost', - database: 'foodb', - }, - role: 'backstage', - plugin: { - testowner: { - role: 'backstage-plugin', - }, - }, - }, - }, - }), - ); - await testManager.forPlugin('testowner').getClient(); - - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig] = mockCalls[0]; - - expect(baseConfig.data.role).toEqual('backstage-plugin'); - }); - - it('Defaults the application_name for postgres clients', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'pg', - connection: {}, - }, - }, - }), - ); - - await testManager.forPlugin('testplugin').getClient(); - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig, _] = mockCalls[0]; - expect(baseConfig.get()).toMatchObject({ - connection: { - application_name: 'backstage_plugin_testplugin', - }, - }); - }); - - it('Allows manually setting the application_name for postgres clients', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'pg', - connection: { - application_name: 'backstage_custom_app_name', - }, - }, - }, - }), - ); - - await testManager.forPlugin('testplugin').getClient(); - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig, overrides] = mockCalls[0]; - expect(baseConfig.get().connection.application_name).toBe( - 'backstage_custom_app_name', - ); - expect(overrides.connection.application_name).toBeUndefined(); - }); - - it('Allows manually setting the application_name for individual plugin client', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'pg', - connection: { - host: 'localhost', - user: 'foo', - password: 'bar', - database: 'foodb', - application_name: 'backstage_custom_app_name', - }, - plugin: { - overrideplugin: { - connection: { - application_name: 'custom_plugin', - }, - }, - }, - }, - }, - }), - ); - - await testManager.forPlugin('overrideplugin').getClient(); - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [baseConfig, _] = mockCalls[0]; - expect(baseConfig.get().connection.application_name).toBe( - 'custom_plugin', - ); + expect((await impl2.forPlugin('plugin1')).migrations).toEqual({ + skip: true, }); }); }); diff --git a/packages/backend-common/src/database/DatabaseManager.ts b/packages/backend-common/src/database/DatabaseManager.ts index 8a4a4b50fa..dd36cc3b4a 100644 --- a/packages/backend-common/src/database/DatabaseManager.ts +++ b/packages/backend-common/src/database/DatabaseManager.ts @@ -14,28 +14,19 @@ * limitations under the License. */ -import { Config, ConfigReader } from '@backstage/config'; -import { JsonObject } from '@backstage/types'; -import { Knex } from 'knex'; -import { merge, omit } from 'lodash'; -import { mergeDatabaseConfig } from './config'; -import { - createDatabaseClient, - createNameOverride, - createSchemaOverride, - ensureDatabaseExists, - ensureSchemaExists, - normalizeConnection, -} from './connection'; -import { PluginDatabaseManager } from './types'; -import path from 'path'; import { DatabaseService, LifecycleService, LoggerService, PluginMetadataService, } from '@backstage/backend-plugin-api'; +import { Config } from '@backstage/config'; import { stringifyError } from '@backstage/errors'; +import { Knex } from 'knex'; +import { MysqlConnector } from './connectors/mysql'; +import { PgConnector } from './connectors/postgres'; +import { Sqlite3Connector } from './connectors/sqlite3'; +import { Connector, PluginDatabaseManager } from './types'; /** * Provides a config lookup path for a plugin's config block. @@ -63,40 +54,12 @@ export type LegacyRootDatabaseService = { }; /** - * Manages database connections for Backstage backend plugins. - * - * @public - * @remarks - * - * The database manager allows the user to set connection and client settings on - * a per pluginId basis by defining a database config block under - * `plugin.<pluginId>` in addition to top level defaults. Optionally, a user may - * set `prefix` which is used to prefix generated database names if config is - * not provided. + * Testable implementation class for {@link DatabaseManager} below. */ -export class DatabaseManager implements LegacyRootDatabaseService { - /** - * Creates a {@link DatabaseManager} from `backend.database` config. - * - * @param config - The loaded application configuration. - * @param options - An optional configuration object. - */ - static fromConfig( - config: Config, - options?: DatabaseManagerOptions, - ): DatabaseManager { - const databaseConfig = config.getConfig('backend.database'); - - return new DatabaseManager( - databaseConfig, - databaseConfig.getOptionalString('prefix'), - options, - ); - } - - private constructor( +export class DatabaseManagerImpl implements LegacyRootDatabaseService { + constructor( private readonly config: Config, - private readonly prefix: string = 'backstage_plugin_', + private readonly connectors: Record<string, Connector>, private readonly options?: DatabaseManagerOptions, private readonly databaseCache: Map<string, Promise<Knex>> = new Map(), ) {} @@ -115,52 +78,18 @@ export class DatabaseManager implements LegacyRootDatabaseService { pluginMetadata: PluginMetadataService; }, ): PluginDatabaseManager { - const getClient = () => this.getDatabase(pluginId, deps); + const client = this.getClientType(pluginId).client; + const connector = this.connectors[client]; + if (!connector) { + throw new Error( + `Unsupported database client type '${client}' specified for plugin '${pluginId}'`, + ); + } + const getClient = () => this.getDatabase(pluginId, connector, deps); const migrations = { skip: false, ...this.options?.migrations }; return { getClient, migrations }; } - /** - * Provides the canonical database name for a given plugin. - * - * This method provides the effective database name which is determined using global - * and plugin specific database config. If no explicit database name is configured - * and `pluginDivisionMode` is not `schema`, this method will provide a generated name - * which is the pluginId prefixed with 'backstage_plugin_'. If `pluginDivisionMode` is - * `schema`, it will fallback to using the default database for the knex instance. - * - * @param pluginId - Lookup the database name for given plugin - * @returns String representing the plugin's database name - */ - private getDatabaseName(pluginId: string): string | undefined { - const connection = this.getConnectionConfig(pluginId); - - if (this.getClientType(pluginId).client.includes('sqlite3')) { - const sqliteFilename: string | undefined = ( - connection as Knex.Sqlite3ConnectionConfig - ).filename; - - if (sqliteFilename === ':memory:') { - return sqliteFilename; - } - - const sqliteDirectory = - (connection as { directory?: string }).directory ?? '.'; - - return path.join(sqliteDirectory, sqliteFilename ?? `${pluginId}.sqlite`); - } - - const databaseName = (connection as Knex.ConnectionConfig)?.database; - - // `pluginDivisionMode` as `schema` should use overridden databaseName if supplied or fallback to default knex database - if (this.getPluginDivisionModeConfig() === 'schema') { - return databaseName; - } - - // all other supported databases should fallback to an auto-prefixed name - return databaseName ?? `${this.prefix}${pluginId}`; - } - /** * Provides the client type which should be used for a given plugin. * @@ -188,143 +117,6 @@ export class DatabaseManager implements LegacyRootDatabaseService { }; } - private getRoleConfig(pluginId: string): string | undefined { - return ( - this.config.getOptionalString(`${pluginPath(pluginId)}.role`) ?? - this.config.getOptionalString('role') - ); - } - - /** - * Provides the knexConfig which should be used for a given plugin. - * - * @param pluginId - Plugin to get the knexConfig for - * @returns The merged knexConfig value or undefined if it isn't specified - */ - private getAdditionalKnexConfig(pluginId: string): JsonObject | undefined { - const pluginConfig = this.config - .getOptionalConfig(`${pluginPath(pluginId)}.knexConfig`) - ?.get<JsonObject>(); - - const baseConfig = this.config - .getOptionalConfig('knexConfig') - ?.get<JsonObject>(); - - return merge(baseConfig, pluginConfig); - } - - private getEnsureExistsConfig(pluginId: string): boolean { - const baseConfig = this.config.getOptionalBoolean('ensureExists') ?? true; - return ( - this.config.getOptionalBoolean(`${pluginPath(pluginId)}.ensureExists`) ?? - baseConfig - ); - } - - private getPluginDivisionModeConfig(): string { - return this.config.getOptionalString('pluginDivisionMode') ?? 'database'; - } - - /** - * Provides a Knex connection plugin config by combining base and plugin - * config. - * - * This method provides a baseConfig for a plugin database connector. If the - * client type has not been overridden, the global connection config will be - * included with plugin specific config as the base. Values from the plugin - * connection take precedence over the base. Base database name is omitted for - * all supported databases excluding SQLite unless `pluginDivisionMode` is set - * to `schema`. - */ - private getConnectionConfig(pluginId: string): Knex.StaticConnectionConfig { - const { client, overridden } = this.getClientType(pluginId); - - let baseConnection = normalizeConnection( - this.config.get('connection'), - this.config.getString('client'), - ); - - if ( - client.includes('sqlite3') && - 'filename' in baseConnection && - baseConnection.filename !== ':memory:' - ) { - throw new Error( - '`connection.filename` is not supported for the base sqlite connection. Prefer `connection.directory` or provide a filename for the plugin connection instead.', - ); - } - - // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`. The - // `database` property from the base connection is omitted unless `pluginDivisionMode` - // is set to `schema`. SQLite3's `filename` property is an exception as this is used as a - // directory elsewhere so we preserve `filename`. - if (this.getPluginDivisionModeConfig() !== 'schema') { - baseConnection = omit(baseConnection, 'database'); - } - - // get and normalize optional plugin specific database connection - const connection = normalizeConnection( - this.config.getOptional(`${pluginPath(pluginId)}.connection`), - client, - ); - - if (client === 'pg') { - ( - baseConnection as Knex.PgConnectionConfig - ).application_name ||= `backstage_plugin_${pluginId}`; - } - - return { - // include base connection if client type has not been overridden - ...(overridden ? {} : baseConnection), - ...connection, - } as Knex.StaticConnectionConfig; - } - - /** - * Provides a Knex database config for a given plugin. - * - * This method provides a Knex configuration object along with the plugin's - * client type. - * - * @param pluginId - The plugin that the database config should correspond with - */ - private getConfigForPlugin(pluginId: string): Knex.Config { - const { client } = this.getClientType(pluginId); - const role = this.getRoleConfig(pluginId); - - return { - ...this.getAdditionalKnexConfig(pluginId), - client, - connection: this.getConnectionConfig(pluginId), - ...(role && { role }), - }; - } - - /** - * Provides a partial `Knex.Config` database schema override for a given - * plugin. - * - * @param pluginId - Target plugin to get database schema override - * @returns Partial `Knex.Config` with database schema override - */ - private getSchemaOverrides(pluginId: string): Knex.Config | undefined { - return createSchemaOverride(this.getClientType(pluginId).client, pluginId); - } - - /** - * Provides a partial `Knex.Config`â€ĸ database name override for a given plugin. - * - * @param pluginId - Target plugin to get database name override - * @returns Partial `Knex.Config` with database name override - */ - private getDatabaseOverrides(pluginId: string): Knex.Config { - const databaseName = this.getDatabaseName(pluginId); - return databaseName - ? createNameOverride(this.getClientType(pluginId).client, databaseName) - : {}; - } - /** * Provides a scoped Knex client for a plugin as per application config. * @@ -334,6 +126,7 @@ export class DatabaseManager implements LegacyRootDatabaseService { */ private async getDatabase( pluginId: string, + connector: Connector, deps?: { lifecycle: LifecycleService; pluginMetadata: PluginMetadataService; @@ -343,55 +136,13 @@ export class DatabaseManager implements LegacyRootDatabaseService { return this.databaseCache.get(pluginId)!; } - const clientPromise = Promise.resolve().then(async () => { - const pluginConfig = new ConfigReader( - this.getConfigForPlugin(pluginId) as JsonObject, - ); - - const databaseName = this.getDatabaseName(pluginId); - if (databaseName && this.getEnsureExistsConfig(pluginId)) { - try { - await ensureDatabaseExists(pluginConfig, databaseName); - } catch (error) { - throw new Error( - `Failed to connect to the database to make sure that '${databaseName}' exists, ${error}`, - ); - } - } - - let schemaOverrides; - if (this.getPluginDivisionModeConfig() === 'schema') { - schemaOverrides = this.getSchemaOverrides(pluginId); - if (this.getEnsureExistsConfig(pluginId)) { - try { - await ensureSchemaExists(pluginConfig, pluginId); - } catch (error) { - throw new Error( - `Failed to connect to the database to make sure that schema for plugin '${pluginId}' exists, ${error}`, - ); - } - } - } - - const databaseClientOverrides = mergeDatabaseConfig( - {}, - this.getDatabaseOverrides(pluginId), - schemaOverrides, - ); - - const client = createDatabaseClient( - pluginConfig, - databaseClientOverrides, - deps, - ); - if (process.env.NODE_ENV !== 'test') { - this.startKeepaliveLoop(pluginId, client); - } - return client; - }); - + const clientPromise = connector.getClient(pluginId, deps); this.databaseCache.set(pluginId, clientPromise); + if (process.env.NODE_ENV !== 'test') { + clientPromise.then(client => this.startKeepaliveLoop(pluginId, client)); + } + return clientPromise; } @@ -419,3 +170,84 @@ export class DatabaseManager implements LegacyRootDatabaseService { }, 60 * 1000); } } + +// NOTE: This class looks odd but is kept around for API compatibility reasons +/** + * Manages database connections for Backstage backend plugins. + * + * @public + * @remarks + * + * The database manager allows the user to set connection and client settings on + * a per pluginId basis by defining a database config block under + * `plugin.<pluginId>` in addition to top level defaults. Optionally, a user may + * set `prefix` which is used to prefix generated database names if config is + * not provided. + */ +export class DatabaseManager implements LegacyRootDatabaseService { + /** + * Creates a {@link DatabaseManager} from `backend.database` config. + * + * @param config - The loaded application configuration. + * @param options - An optional configuration object. + */ + static fromConfig( + config: Config, + options?: DatabaseManagerOptions, + ): DatabaseManager { + const databaseConfig = config.getConfig('backend.database'); + const prefix = + databaseConfig.getOptionalString('prefix') || 'backstage_plugin_'; + return new DatabaseManager( + new DatabaseManagerImpl( + databaseConfig, + { + pg: new PgConnector(databaseConfig, prefix), + sqlite3: new Sqlite3Connector(databaseConfig), + 'better-sqlite3': new Sqlite3Connector(databaseConfig), + mysql: new MysqlConnector(databaseConfig, prefix), + mysql2: new MysqlConnector(databaseConfig, prefix), + }, + options, + ), + ); + } + + private constructor(private readonly impl: DatabaseManagerImpl) {} + + /** + * Generates a PluginDatabaseManager for consumption by plugins. + * + * @param pluginId - The plugin that the database manager should be created for. Plugin names + * should be unique as they are used to look up database config overrides under + * `backend.database.plugin`. + */ + forPlugin( + pluginId: string, + deps?: { + lifecycle: LifecycleService; + pluginMetadata: PluginMetadataService; + }, + ): PluginDatabaseManager { + return this.impl.forPlugin(pluginId, deps); + } +} + +/** + * Helper for deleting databases, only exists for backend-test-utils for now. + * + * @public + */ +export async function dropDatabase( + dbConfig: Config, + ...databaseNames: string[] +): Promise<void> { + const client = dbConfig.getString('client'); + const prefix = dbConfig.getOptionalString('prefix') || 'backstage_plugin_'; + + if (client === 'pg') { + await new PgConnector(dbConfig, prefix).dropDatabase(...databaseNames); + } else if (client === 'mysql' || client === 'mysql2') { + await new MysqlConnector(dbConfig, prefix).dropDatabase(...databaseNames); + } +} diff --git a/packages/backend-common/src/database/connection.test.ts b/packages/backend-common/src/database/connection.test.ts deleted file mode 100644 index 3028d46cae..0000000000 --- a/packages/backend-common/src/database/connection.test.ts +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { ConfigReader } from '@backstage/config'; -import { - createDatabaseClient, - createNameOverride, - createSchemaOverride, - dropDatabase, - ensureSchemaExists, - parseConnectionString, -} from './connection'; -import { mysqlConnector, pgConnector } from './connectors'; - -const mocked = (f: Function) => f as jest.Mock; - -jest.mock('./connectors', () => { - const connectors = jest.requireActual('./connectors'); - return { - ...connectors, - mysqlConnector: { - ...connectors.mysqlConnector, - dropDatabase: jest.fn(), - }, - pgConnector: { - ...connectors.pgConnector, - dropDatabase: jest.fn(), - ensureSchemaExists: jest.fn(), - }, - }; -}); - -describe('database connection', () => { - describe('createDatabaseClient', () => { - it('returns a postgres connection', () => { - expect( - createDatabaseClient( - new ConfigReader({ - client: 'pg', - connection: { - host: 'acme', - user: 'foo', - password: 'bar', - database: 'foodb', - }, - }), - ), - ).toBeTruthy(); - }); - - it('returns an sqlite connection', () => { - expect( - createDatabaseClient( - new ConfigReader({ - client: 'better-sqlite3', - connection: ':memory:', - }), - ), - ).toBeTruthy(); - }); - - it('returns a mysql connection', () => { - expect(() => - createDatabaseClient( - new ConfigReader({ - client: 'mysql2', - connection: { - host: '127.0.0.1', - user: 'foo', - password: 'bar', - database: 'dbname', - }, - }), - ), - ).toBeTruthy(); - }); - - it('accepts overrides', () => { - expect( - createDatabaseClient( - new ConfigReader({ - client: 'pg', - connection: { - host: 'acme', - user: 'foo', - password: 'bar', - database: 'foodb', - }, - }), - { - connection: { - database: 'foo', - }, - }, - ), - ).toBeTruthy(); - }); - - it('throws an error without a client', () => { - expect(() => - createDatabaseClient( - new ConfigReader({ - connection: '', - }), - ), - ).toThrow(); - }); - - it('throws an error without a connection', () => { - expect(() => - createDatabaseClient( - new ConfigReader({ - client: 'pg', - }), - ), - ).toThrow(); - }); - }); - - describe('createNameOverride', () => { - it('returns Knex config for postgres', () => { - expect(createNameOverride('pg', 'testpg')).toHaveProperty( - 'connection.database', - 'testpg', - ); - }); - - it('returns Knex config for sqlite', () => { - expect(createNameOverride('better-sqlite3', 'testsqlite')).toHaveProperty( - 'connection.filename', - 'testsqlite', - ); - }); - - it('returns Knex config for mysql', () => { - expect(createNameOverride('mysql', 'testmysql')).toHaveProperty( - 'connection.database', - 'testmysql', - ); - }); - - it('throws an error for unknown connection', () => { - expect(() => createNameOverride('unknown', 'testname')).toThrow(); - }); - }); - - describe('parseConnectionString', () => { - it('returns parsed Knex.StaticConnectionConfig for postgres', () => { - expect( - parseConnectionString('postgresql://foo:bar@acme:5432/foodb', 'pg'), - ).toHaveProperty('database', 'foodb'); - }); - - it('returns parsed Knex.StaticConnectionConfig for mysql2', () => { - expect( - parseConnectionString('mysql://foo:bar@acme:3306/foodb', 'mysql2'), - ).toHaveProperty('database', 'foodb'); - }); - - it('throws an error if client hint is not provided', () => { - expect(() => parseConnectionString('sqlite://')).toThrow(); - }); - }); - - describe('createSchemaOverride', () => { - it('returns Knex config for postgres', () => { - expect(createSchemaOverride('pg', 'testpg')).toHaveProperty( - 'searchPath', - ['testpg'], - ); - }); - - it('throws error for sqlite', () => { - expect( - createSchemaOverride('better-sqlite3', 'testsqlite'), - ).toBeUndefined(); - }); - - it('returns Knex config for mysql', () => { - expect(createSchemaOverride('mysql', 'testmysql')).toBeUndefined(); - }); - - it('throws an error for unknown connection', () => { - expect(createSchemaOverride('unknown', 'testname')).toBeUndefined(); - }); - }); - - describe('ensureSchemaExists', () => { - it('returns successfully with pg client', async () => { - await ensureSchemaExists( - new ConfigReader({ - client: 'pg', - schema: 'catalog', - connection: 'postgresql://testuser:testpass@acme:5432/userdbname', - }), - 'catalog', - ); - - const mockCalls = mocked( - pgConnector.ensureSchemaExists as Function, - ).mock.calls.splice(-1); - const [baseConfig, schemaName] = mockCalls[0]; - - expect(baseConfig.get()).toMatchObject({ - client: 'pg', - connection: 'postgresql://testuser:testpass@acme:5432/userdbname', - }); - - expect(schemaName).toEqual('catalog'); - }); - - it('throws error for non pg client', () => { - return expect( - ensureSchemaExists( - new ConfigReader({ - client: 'better-sqlite3', - schema: 'catalog', - connection: ':memory:', - }), - 'catalog', - ), - ).resolves.toBeUndefined(); - }); - }); - - describe('dropDatabase', () => { - it('returns successfully with pg client', async () => { - await dropDatabase( - new ConfigReader({ - client: 'pg', - schema: 'catalog', - connection: 'postgresql://testuser:testpass@acme:5432/userdbname', - }), - 'backstage_plugin_foobar', - ); - - const mockCalls = mocked( - pgConnector.dropDatabase as Function, - ).mock.calls.splice(-1); - const [baseConfig, databaseName] = mockCalls[0]; - - expect(baseConfig.get()).toMatchObject({ - client: 'pg', - connection: 'postgresql://testuser:testpass@acme:5432/userdbname', - }); - - expect(databaseName).toEqual('backstage_plugin_foobar'); - }); - - it('returns successfully with mysql client', async () => { - await dropDatabase( - new ConfigReader({ - client: 'mysql2', - connection: { - host: '127.0.0.1', - user: 'foo', - password: 'bar', - database: 'dbname', - }, - }), - 'backstage_plugin_foobar', - ); - - const mockCalls = mocked( - mysqlConnector.dropDatabase as Function, - ).mock.calls.splice(-1); - const [baseConfig, databaseName] = mockCalls[0]; - - expect(baseConfig.get()).toMatchObject({ - client: 'mysql2', - connection: { - host: '127.0.0.1', - user: 'foo', - password: 'bar', - database: 'dbname', - }, - }); - - expect(databaseName).toEqual('backstage_plugin_foobar'); - }); - - it('does nothing in other database drivers', () => { - return expect( - dropDatabase( - new ConfigReader({ - client: 'better-sqlite3', - schema: 'catalog', - connection: ':memory:', - }), - 'catalog', - ), - ).resolves.toBeUndefined(); - }); - }); -}); diff --git a/packages/backend-common/src/database/connection.ts b/packages/backend-common/src/database/connection.ts deleted file mode 100644 index c0e031516f..0000000000 --- a/packages/backend-common/src/database/connection.ts +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Config } from '@backstage/config'; -import { JsonObject } from '@backstage/types'; -import { InputError } from '@backstage/errors'; -import knexFactory, { Knex } from 'knex'; -import limiterFactory from 'p-limit'; -import { mergeDatabaseConfig } from './config'; -import { DatabaseConnector } from './types'; - -import { mysqlConnector, pgConnector, sqlite3Connector } from './connectors'; -import { - LifecycleService, - PluginMetadataService, -} from '@backstage/backend-plugin-api'; - -type DatabaseClient = - | 'pg' - | 'better-sqlite3' - | 'sqlite3' - | 'mysql' - | 'mysql2' - | string; - -// This limits the number of concurrent CREATE DATABASE and CREATE SCHEMA -// commands, globally, to just one. This is overly defensive, and was added as -// an attempt to counteract the pool issues on recent node versions. See -// https://github.com/backstage/backstage/pull/19988 -const ddlLimiter = limiterFactory(1); - -/** - * Mapping of client type to supported database connectors - * - * Database connectors can be aliased here, for example mysql2 uses - * the same connector as mysql. - */ -const ConnectorMapping: Record<DatabaseClient, DatabaseConnector> = { - pg: pgConnector, - 'better-sqlite3': sqlite3Connector, - sqlite3: sqlite3Connector, - mysql: mysqlConnector, - mysql2: mysqlConnector, -}; - -/** - * Creates a knex database connection - * - * @public - * @param dbConfig - The database config - * @param overrides - Additional options to merge with the config - */ -export function createDatabaseClient( - dbConfig: Config, - overrides?: Partial<Knex.Config>, - deps?: { - lifecycle: LifecycleService; - pluginMetadata: PluginMetadataService; - }, -) { - const client: DatabaseClient = dbConfig.getString('client'); - - return ( - ConnectorMapping[client]?.createClient(dbConfig, overrides, deps) ?? - knexFactory(mergeDatabaseConfig(dbConfig.get(), overrides)) - ); -} - -/** - * Ensures that the given databases all exist, creating them if they do not. - * - * @public - */ -export async function ensureDatabaseExists( - dbConfig: Config, - ...databases: Array<string> -): Promise<void> { - const client: DatabaseClient = dbConfig.getString('client'); - - return await ddlLimiter(() => - ConnectorMapping[client]?.ensureDatabaseExists?.(dbConfig, ...databases), - ); -} - -/** - * Drops the given databases. - * - * @public - */ -export async function dropDatabase( - dbConfig: Config, - ...databases: Array<string> -): Promise<void> { - const client: DatabaseClient = dbConfig.getString('client'); - - return await ddlLimiter(() => - ConnectorMapping[client]?.dropDatabase?.(dbConfig, ...databases), - ); -} - -/** - * Ensures that the given schemas all exist, creating them if they do not. - * - * @public - */ -export async function ensureSchemaExists( - dbConfig: Config, - ...schemas: Array<string> -): Promise<void> { - const client: DatabaseClient = dbConfig.getString('client'); - - return await ddlLimiter(() => - ConnectorMapping[client]?.ensureSchemaExists?.(dbConfig, ...schemas), - ); -} - -/** - * Provides a `Knex.Config` object with the provided database name for a given - * client. - */ -export function createNameOverride( - client: string, - name: string, -): Partial<Knex.Config> { - try { - return ConnectorMapping[client].createNameOverride(name); - } catch (e) { - throw new InputError( - `Unable to create database name override for '${client}' connector`, - e, - ); - } -} - -/** - * Provides a `Knex.Config` object with the provided database schema for a given - * client. Currently only supported by `pg`. - */ -export function createSchemaOverride( - client: string, - name: string, -): Partial<Knex.Config | undefined> { - try { - return ConnectorMapping[client]?.createSchemaOverride?.(name); - } catch (e) { - throw new InputError( - `Unable to create database schema override for '${client}' connector`, - e, - ); - } -} - -/** - * Parses a connection string for a given client and provides a connection config. - */ -export function parseConnectionString( - connectionString: string, - client?: string, -): Knex.StaticConnectionConfig { - if (typeof client === 'undefined' || client === null) { - throw new InputError( - 'Database connection string client type auto-detection is not yet supported.', - ); - } - - try { - return ConnectorMapping[client].parseConnectionString(connectionString); - } catch (e) { - throw new InputError( - `Unable to parse connection string for '${client}' connector`, - ); - } -} - -/** - * Normalizes a connection config or string into an object which can be passed - * to Knex. - */ -export function normalizeConnection( - connection: Knex.StaticConnectionConfig | JsonObject | string | undefined, - client: string, -): Partial<Knex.StaticConnectionConfig> { - if (typeof connection === 'undefined' || connection === null) { - return {}; - } - - return typeof connection === 'string' || connection instanceof String - ? parseConnectionString(connection as string, client) - : connection; -} diff --git a/packages/backend-common/src/database/connectors/defaultNameOverride.test.ts b/packages/backend-common/src/database/connectors/defaultNameOverride.test.ts index 1da8e6c11e..59f7b5b137 100644 --- a/packages/backend-common/src/database/connectors/defaultNameOverride.test.ts +++ b/packages/backend-common/src/database/connectors/defaultNameOverride.test.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import defaultNameOverride from './defaultNameOverride'; describe('defaultNameOverride()', () => { diff --git a/packages/backend-common/src/database/connectors/defaultNameOverride.ts b/packages/backend-common/src/database/connectors/defaultNameOverride.ts index bc467eb7f6..3f610c51c6 100644 --- a/packages/backend-common/src/database/connectors/defaultNameOverride.ts +++ b/packages/backend-common/src/database/connectors/defaultNameOverride.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Knex } from 'knex'; /** diff --git a/packages/backend-common/src/database/connectors/defaultSchemaOverride.test.ts b/packages/backend-common/src/database/connectors/defaultSchemaOverride.test.ts index 2db5ecd454..505b9051f7 100644 --- a/packages/backend-common/src/database/connectors/defaultSchemaOverride.test.ts +++ b/packages/backend-common/src/database/connectors/defaultSchemaOverride.test.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import defaultSchemaOverride from './defaultSchemaOverride'; describe('defaultNameOverride()', () => { diff --git a/packages/backend-common/src/database/connectors/defaultSchemaOverride.ts b/packages/backend-common/src/database/connectors/defaultSchemaOverride.ts index 4e76308542..85c6f1ec65 100644 --- a/packages/backend-common/src/database/connectors/defaultSchemaOverride.ts +++ b/packages/backend-common/src/database/connectors/defaultSchemaOverride.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Knex } from 'knex'; /** diff --git a/packages/backend-common/src/database/connectors/index.ts b/packages/backend-common/src/database/connectors/index.ts index df84ec66ba..9d18e7460a 100644 --- a/packages/backend-common/src/database/connectors/index.ts +++ b/packages/backend-common/src/database/connectors/index.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + export * from './mysql'; export * from './postgres'; export * from './sqlite3'; diff --git a/packages/backend-common/src/database/config.test.ts b/packages/backend-common/src/database/connectors/mergeDatabaseConfig.test.ts similarity index 97% rename from packages/backend-common/src/database/config.test.ts rename to packages/backend-common/src/database/connectors/mergeDatabaseConfig.test.ts index a5bf4a79d2..84177a1208 100644 --- a/packages/backend-common/src/database/config.test.ts +++ b/packages/backend-common/src/database/connectors/mergeDatabaseConfig.test.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { mergeDatabaseConfig } from './config'; +import { mergeDatabaseConfig } from './mergeDatabaseConfig'; -describe('config', () => { +describe('mergeDatabaseConfig', () => { describe('mergeDatabaseConfig', () => { it('does not mutate the input object', () => { const input = { diff --git a/packages/backend-common/src/database/config.ts b/packages/backend-common/src/database/connectors/mergeDatabaseConfig.ts similarity index 100% rename from packages/backend-common/src/database/config.ts rename to packages/backend-common/src/database/connectors/mergeDatabaseConfig.ts diff --git a/packages/backend-common/src/database/connectors/mysql.ts b/packages/backend-common/src/database/connectors/mysql.ts index a4bac3f7e1..1ad8b0b3be 100644 --- a/packages/backend-common/src/database/connectors/mysql.ts +++ b/packages/backend-common/src/database/connectors/mysql.ts @@ -14,14 +14,23 @@ * limitations under the License. */ -import knexFactory, { Knex } from 'knex'; -import yn from 'yn'; - -import { Config } from '@backstage/config'; +import { + LifecycleService, + PluginMetadataService, +} from '@backstage/backend-plugin-api'; +import { Config, ConfigReader } from '@backstage/config'; import { InputError } from '@backstage/errors'; -import { mergeDatabaseConfig } from '../config'; -import { DatabaseConnector } from '../types'; +import { JsonObject } from '@backstage/types'; +import knexFactory, { Knex } from 'knex'; +import { merge, omit } from 'lodash'; +import limiterFactory from 'p-limit'; +import yn from 'yn'; +import { Connector, DatabaseConnector } from '../types'; import defaultNameOverride from './defaultNameOverride'; +import { mergeDatabaseConfig } from './mergeDatabaseConfig'; + +// Limits the number of concurrent DDL operations to 1 +const ddlLimiter = limiterFactory(1); /** * Creates a knex mysql database connection @@ -169,7 +178,7 @@ export async function ensureMysqlDatabaseExists( let lastErr: Error | undefined = undefined; for (let i = 0; i < 3; i++) { try { - return await ensureDatabase(database); + return await ddlLimiter(() => ensureDatabase(database)); } catch (err) { lastErr = err; } @@ -209,7 +218,7 @@ export async function dropMysqlDatabase( }; await Promise.all( databases.map(async database => { - return await dropDatabase(database); + return await ddlLimiter(() => dropDatabase(database)); }), ); } finally { @@ -229,3 +238,243 @@ export const mysqlConnector: DatabaseConnector = Object.freeze({ parseConnectionString: parseMysqlConnectionString, dropDatabase: dropMysqlDatabase, }); + +/** + * Provides a config lookup path for a plugin's config block. + */ +function pluginPath(pluginId: string): string { + return `plugin.${pluginId}`; +} + +function normalizeConnection( + connection: Knex.StaticConnectionConfig | JsonObject | string | undefined, + client: string, +): Partial<Knex.StaticConnectionConfig> { + if (typeof connection === 'undefined' || connection === null) { + return {}; + } + + return typeof connection === 'string' || connection instanceof String + ? mysqlConnector.parseConnectionString(connection as string, client) + : connection; +} + +function createNameOverride( + client: string, + name: string, +): Partial<Knex.Config> { + try { + return mysqlConnector.createNameOverride(name); + } catch (e) { + throw new InputError( + `Unable to create database name override for '${client}' connector`, + e, + ); + } +} + +export class MysqlConnector implements Connector { + constructor( + private readonly config: Config, + private readonly prefix: string, + ) {} + + async getClient( + pluginId: string, + deps?: { + lifecycle: LifecycleService; + pluginMetadata: PluginMetadataService; + }, + ): Promise<Knex> { + const pluginConfig = new ConfigReader( + this.getConfigForPlugin(pluginId) as JsonObject, + ); + + const databaseName = this.getDatabaseName(pluginId); + if (databaseName && this.getEnsureExistsConfig(pluginId)) { + try { + await mysqlConnector.ensureDatabaseExists!(pluginConfig, databaseName); + } catch (error) { + throw new Error( + `Failed to connect to the database to make sure that '${databaseName}' exists, ${error}`, + ); + } + } + + const pluginDivisionMode = this.getPluginDivisionModeConfig(); + if (pluginDivisionMode !== 'database') { + throw new Error( + `The MySQL driver does not suppoert plugin division mode '${pluginDivisionMode}'`, + ); + } + + const databaseClientOverrides = mergeDatabaseConfig( + {}, + this.getDatabaseOverrides(pluginId), + ); + + const client = mysqlConnector.createClient( + pluginConfig, + databaseClientOverrides, + deps, + ); + + return client; + } + + async dropDatabase(...databaseNames: string[]): Promise<void> { + return await dropMysqlDatabase(this.config, ...databaseNames); + } + + /** + * Provides the canonical database name for a given plugin. + * + * This method provides the effective database name which is determined using + * global and plugin specific database config. If no explicit database name, + * this method will provide a generated name which is the pluginId prefixed + * with 'backstage_plugin_'. + * + * @param pluginId - Lookup the database name for given plugin + * @returns String representing the plugin's database name + */ + private getDatabaseName(pluginId: string): string | undefined { + const connection = this.getConnectionConfig(pluginId); + const databaseName = (connection as Knex.ConnectionConfig)?.database; + return databaseName ?? `${this.prefix}${pluginId}`; + } + + /** + * Provides the client type which should be used for a given plugin. + * + * The client type is determined by plugin specific config if present. + * Otherwise the base client is used as the fallback. + * + * @param pluginId - Plugin to get the client type for + * @returns Object with client type returned as `client` and boolean + * representing whether or not the client was overridden as + * `overridden` + */ + private getClientType(pluginId: string): { + client: string; + overridden: boolean; + } { + const pluginClient = this.config.getOptionalString( + `${pluginPath(pluginId)}.client`, + ); + + const baseClient = this.config.getString('client'); + const client = pluginClient ?? baseClient; + return { + client, + overridden: client !== baseClient, + }; + } + + private getRoleConfig(pluginId: string): string | undefined { + return ( + this.config.getOptionalString(`${pluginPath(pluginId)}.role`) ?? + this.config.getOptionalString('role') + ); + } + + /** + * Provides the knexConfig which should be used for a given plugin. + * + * @param pluginId - Plugin to get the knexConfig for + * @returns The merged knexConfig value or undefined if it isn't specified + */ + private getAdditionalKnexConfig(pluginId: string): JsonObject | undefined { + const pluginConfig = this.config + .getOptionalConfig(`${pluginPath(pluginId)}.knexConfig`) + ?.get<JsonObject>(); + + const baseConfig = this.config + .getOptionalConfig('knexConfig') + ?.get<JsonObject>(); + + return merge(baseConfig, pluginConfig); + } + + private getEnsureExistsConfig(pluginId: string): boolean { + const baseConfig = this.config.getOptionalBoolean('ensureExists') ?? true; + return ( + this.config.getOptionalBoolean(`${pluginPath(pluginId)}.ensureExists`) ?? + baseConfig + ); + } + + private getPluginDivisionModeConfig(): string { + return this.config.getOptionalString('pluginDivisionMode') ?? 'database'; + } + + /** + * Provides a Knex connection plugin config by combining base and plugin + * config. + * + * This method provides a baseConfig for a plugin database connector. If the + * client type has not been overridden, the global connection config will be + * included with plugin specific config as the base. Values from the plugin + * connection take precedence over the base. Base database name is omitted + * unless `pluginDivisionMode` is set to `schema`. + */ + private getConnectionConfig(pluginId: string): Knex.StaticConnectionConfig { + const { client, overridden } = this.getClientType(pluginId); + + let baseConnection = normalizeConnection( + this.config.get('connection'), + this.config.getString('client'), + ); + + // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`. The + // `database` property from the base connection is omitted unless `pluginDivisionMode` + // is set to `schema`. + if (this.getPluginDivisionModeConfig() !== 'schema') { + baseConnection = omit(baseConnection, 'database'); + } + + // get and normalize optional plugin specific database connection + const connection = normalizeConnection( + this.config.getOptional(`${pluginPath(pluginId)}.connection`), + client, + ); + + return { + // include base connection if client type has not been overridden + ...(overridden ? {} : baseConnection), + ...connection, + } as Knex.StaticConnectionConfig; + } + + /** + * Provides a Knex database config for a given plugin. + * + * This method provides a Knex configuration object along with the plugin's + * client type. + * + * @param pluginId - The plugin that the database config should correspond with + */ + private getConfigForPlugin(pluginId: string): Knex.Config { + const { client } = this.getClientType(pluginId); + const role = this.getRoleConfig(pluginId); + + return { + ...this.getAdditionalKnexConfig(pluginId), + client, + connection: this.getConnectionConfig(pluginId), + ...(role && { role }), + }; + } + + /** + * Provides a partial `Knex.Config`â€ĸ database name override for a given plugin. + * + * @param pluginId - Target plugin to get database name override + * @returns Partial `Knex.Config` with database name override + */ + private getDatabaseOverrides(pluginId: string): Knex.Config { + const databaseName = this.getDatabaseName(pluginId); + return databaseName + ? createNameOverride(this.getClientType(pluginId).client, databaseName) + : {}; + } +} diff --git a/packages/backend-common/src/database/connectors/postgres.test.ts b/packages/backend-common/src/database/connectors/postgres.test.ts index 39c9af5ef3..1e07984967 100644 --- a/packages/backend-common/src/database/connectors/postgres.test.ts +++ b/packages/backend-common/src/database/connectors/postgres.test.ts @@ -16,8 +16,8 @@ import { Config, ConfigReader } from '@backstage/config'; import { - createPgDatabaseClient, buildPgDatabaseConfig, + createPgDatabaseClient, getPgConnectionConfig, parsePgConnectionString, } from './postgres'; diff --git a/packages/backend-common/src/database/connectors/postgres.ts b/packages/backend-common/src/database/connectors/postgres.ts index dc9b19f6ad..b81bfd5b50 100644 --- a/packages/backend-common/src/database/connectors/postgres.ts +++ b/packages/backend-common/src/database/connectors/postgres.ts @@ -14,15 +14,24 @@ * limitations under the License. */ +import { + LifecycleService, + PluginMetadataService, +} from '@backstage/backend-plugin-api'; +import { Config, ConfigReader } from '@backstage/config'; +import { ForwardedError, InputError } from '@backstage/errors'; +import { JsonObject } from '@backstage/types'; import knexFactory, { Knex } from 'knex'; - -import { Config } from '@backstage/config'; -import { ForwardedError } from '@backstage/errors'; -import { mergeDatabaseConfig } from '../config'; -import { DatabaseConnector } from '../types'; +import { merge, omit } from 'lodash'; +import limiterFactory from 'p-limit'; +import { Client } from 'pg'; +import { Connector, DatabaseConnector } from '../types'; import defaultNameOverride from './defaultNameOverride'; import defaultSchemaOverride from './defaultSchemaOverride'; -import { Client } from 'pg'; +import { mergeDatabaseConfig } from './mergeDatabaseConfig'; + +// Limits the number of concurrent DDL operations to 1 +const ddlLimiter = limiterFactory(1); /** * Creates a knex postgres database connection @@ -154,7 +163,7 @@ export async function ensurePgDatabaseExists( let lastErr: Error | undefined = undefined; for (let i = 0; i < 3; i++) { try { - return await ensureDatabase(database); + return await ddlLimiter(() => ensureDatabase(database)); } catch (err) { lastErr = err; } @@ -193,7 +202,9 @@ export async function ensurePgSchemaExists( } }; - await Promise.all(schemas.map(ensureSchema)); + await Promise.all( + schemas.map(database => ddlLimiter(() => ensureSchema(database))), + ); } finally { await admin.destroy(); } @@ -210,18 +221,17 @@ export async function dropPgDatabase( ...databases: Array<string> ) { const admin = createPgDatabaseClient(dbConfig); - await Promise.all( - databases.map(async database => { - await admin.raw(`DROP DATABASE ??`, [database]); - }), - ); + try { + await Promise.all( + databases.map(async database => { + await ddlLimiter(() => admin.raw(`DROP DATABASE ??`, [database])); + }), + ); + } finally { + await admin.destroy(); + } } -/** - * PostgreSQL database connector. - * - * Exposes database connector functionality via an immutable object. - */ export const pgConnector: DatabaseConnector = Object.freeze({ createClient: createPgDatabaseClient, ensureDatabaseExists: ensurePgDatabaseExists, @@ -231,3 +241,302 @@ export const pgConnector: DatabaseConnector = Object.freeze({ parseConnectionString: parsePgConnectionString, dropDatabase: dropPgDatabase, }); + +/** + * Provides a config lookup path for a plugin's config block. + */ +function pluginPath(pluginId: string): string { + return `plugin.${pluginId}`; +} + +function normalizeConnection( + connection: Knex.StaticConnectionConfig | JsonObject | string | undefined, + client: string, +): Partial<Knex.StaticConnectionConfig> { + if (typeof connection === 'undefined' || connection === null) { + return {}; + } + + return typeof connection === 'string' || connection instanceof String + ? pgConnector.parseConnectionString(connection as string, client) + : connection; +} + +function createSchemaOverride( + client: string, + name: string, +): Partial<Knex.Config | undefined> { + try { + return pgConnector.createSchemaOverride?.(name); + } catch (e) { + throw new InputError( + `Unable to create database schema override for '${client}' connector`, + e, + ); + } +} + +function createNameOverride( + client: string, + name: string, +): Partial<Knex.Config> { + try { + return pgConnector.createNameOverride(name); + } catch (e) { + throw new InputError( + `Unable to create database name override for '${client}' connector`, + e, + ); + } +} + +export class PgConnector implements Connector { + constructor( + private readonly config: Config, + private readonly prefix: string, + ) {} + + async getClient( + pluginId: string, + deps?: { + lifecycle: LifecycleService; + pluginMetadata: PluginMetadataService; + }, + ): Promise<Knex> { + const pluginConfig = new ConfigReader( + this.getConfigForPlugin(pluginId) as JsonObject, + ); + + const databaseName = this.getDatabaseName(pluginId); + if (databaseName && this.getEnsureExistsConfig(pluginId)) { + try { + await pgConnector.ensureDatabaseExists!(pluginConfig, databaseName); + } catch (error) { + throw new Error( + `Failed to connect to the database to make sure that '${databaseName}' exists, ${error}`, + ); + } + } + + let schemaOverrides; + if (this.getPluginDivisionModeConfig() === 'schema') { + schemaOverrides = this.getSchemaOverrides(pluginId); + if ( + this.getEnsureSchemaExistsConfig(pluginId) || + this.getEnsureExistsConfig(pluginId) + ) { + try { + await pgConnector.ensureSchemaExists!(pluginConfig, pluginId); + } catch (error) { + throw new Error( + `Failed to connect to the database to make sure that schema for plugin '${pluginId}' exists, ${error}`, + ); + } + } + } + + const databaseClientOverrides = mergeDatabaseConfig( + {}, + this.getDatabaseOverrides(pluginId), + schemaOverrides, + ); + + const client = pgConnector.createClient( + pluginConfig, + databaseClientOverrides, + deps, + ); + + return client; + } + + async dropDatabase(...databaseNames: string[]): Promise<void> { + return await dropPgDatabase(this.config, ...databaseNames); + } + + /** + * Provides the canonical database name for a given plugin. + * + * This method provides the effective database name which is determined using global + * and plugin specific database config. If no explicit database name is configured + * and `pluginDivisionMode` is not `schema`, this method will provide a generated name + * which is the pluginId prefixed with 'backstage_plugin_'. If `pluginDivisionMode` is + * `schema`, it will fallback to using the default database for the knex instance. + * + * @param pluginId - Lookup the database name for given plugin + * @returns String representing the plugin's database name + */ + private getDatabaseName(pluginId: string): string | undefined { + const connection = this.getConnectionConfig(pluginId); + + const databaseName = (connection as Knex.ConnectionConfig)?.database; + + // `pluginDivisionMode` as `schema` should use overridden databaseName if supplied or fallback to default knex database + if (this.getPluginDivisionModeConfig() === 'schema') { + return databaseName; + } + + // all other supported databases should fallback to an auto-prefixed name + return databaseName ?? `${this.prefix}${pluginId}`; + } + + /** + * Provides the client type which should be used for a given plugin. + * + * The client type is determined by plugin specific config if present. + * Otherwise the base client is used as the fallback. + * + * @param pluginId - Plugin to get the client type for + * @returns Object with client type returned as `client` and boolean + * representing whether or not the client was overridden as + * `overridden` + */ + private getClientType(pluginId: string): { + client: string; + overridden: boolean; + } { + const pluginClient = this.config.getOptionalString( + `${pluginPath(pluginId)}.client`, + ); + + const baseClient = this.config.getString('client'); + const client = pluginClient ?? baseClient; + return { + client, + overridden: client !== baseClient, + }; + } + + private getRoleConfig(pluginId: string): string | undefined { + return ( + this.config.getOptionalString(`${pluginPath(pluginId)}.role`) ?? + this.config.getOptionalString('role') + ); + } + + /** + * Provides the knexConfig which should be used for a given plugin. + * + * @param pluginId - Plugin to get the knexConfig for + * @returns The merged knexConfig value or undefined if it isn't specified + */ + private getAdditionalKnexConfig(pluginId: string): JsonObject | undefined { + const pluginConfig = this.config + .getOptionalConfig(`${pluginPath(pluginId)}.knexConfig`) + ?.get<JsonObject>(); + + const baseConfig = this.config + .getOptionalConfig('knexConfig') + ?.get<JsonObject>(); + + return merge(baseConfig, pluginConfig); + } + + private getEnsureExistsConfig(pluginId: string): boolean { + const baseConfig = this.config.getOptionalBoolean('ensureExists') ?? true; + return ( + this.config.getOptionalBoolean(`${pluginPath(pluginId)}.ensureExists`) ?? + baseConfig + ); + } + + private getEnsureSchemaExistsConfig(pluginId: string): boolean { + const baseConfig = + this.config.getOptionalBoolean('ensureSchemaExists') ?? false; + return ( + this.config.getOptionalBoolean( + `${pluginPath(pluginId)}.getEnsureSchemaExistsConfig`, + ) ?? baseConfig + ); + } + + private getPluginDivisionModeConfig(): string { + return this.config.getOptionalString('pluginDivisionMode') ?? 'database'; + } + + /** + * Provides a Knex connection plugin config by combining base and plugin + * config. + * + * This method provides a baseConfig for a plugin database connector. If the + * client type has not been overridden, the global connection config will be + * included with plugin specific config as the base. Values from the plugin + * connection take precedence over the base. Base database name is omitted + * unless `pluginDivisionMode` is set to `schema`. + */ + private getConnectionConfig(pluginId: string): Knex.StaticConnectionConfig { + const { client, overridden } = this.getClientType(pluginId); + + let baseConnection = normalizeConnection( + this.config.get('connection'), + this.config.getString('client'), + ); + + // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`. The + // `database` property from the base connection is omitted unless `pluginDivisionMode` + // is set to `schema`. + if (this.getPluginDivisionModeConfig() !== 'schema') { + baseConnection = omit(baseConnection, 'database'); + } + + // get and normalize optional plugin specific database connection + const connection = normalizeConnection( + this.config.getOptional(`${pluginPath(pluginId)}.connection`), + client, + ); + + ( + baseConnection as Knex.PgConnectionConfig + ).application_name ||= `backstage_plugin_${pluginId}`; + + return { + // include base connection if client type has not been overridden + ...(overridden ? {} : baseConnection), + ...connection, + } as Knex.StaticConnectionConfig; + } + + /** + * Provides a Knex database config for a given plugin. + * + * This method provides a Knex configuration object along with the plugin's + * client type. + * + * @param pluginId - The plugin that the database config should correspond with + */ + private getConfigForPlugin(pluginId: string): Knex.Config { + const { client } = this.getClientType(pluginId); + const role = this.getRoleConfig(pluginId); + + return { + ...this.getAdditionalKnexConfig(pluginId), + client, + connection: this.getConnectionConfig(pluginId), + ...(role && { role }), + }; + } + + /** + * Provides a partial `Knex.Config` database schema override for a given + * plugin. + * + * @param pluginId - Target plugin to get database schema override + * @returns Partial `Knex.Config` with database schema override + */ + private getSchemaOverrides(pluginId: string): Knex.Config | undefined { + return createSchemaOverride(this.getClientType(pluginId).client, pluginId); + } + + /** + * Provides a partial `Knex.Config`â€ĸ database name override for a given plugin. + * + * @param pluginId - Target plugin to get database name override + * @returns Partial `Knex.Config` with database name override + */ + private getDatabaseOverrides(pluginId: string): Knex.Config { + const databaseName = this.getDatabaseName(pluginId); + return databaseName + ? createNameOverride(this.getClientType(pluginId).client, databaseName) + : {}; + } +} diff --git a/packages/backend-common/src/database/connectors/sqlite3.ts b/packages/backend-common/src/database/connectors/sqlite3.ts index 3cb0f57de6..46ff756854 100644 --- a/packages/backend-common/src/database/connectors/sqlite3.ts +++ b/packages/backend-common/src/database/connectors/sqlite3.ts @@ -14,17 +14,20 @@ * limitations under the License. */ -import { Config } from '@backstage/config'; -import { ensureDirSync } from 'fs-extra'; -import knexFactory, { Knex } from 'knex'; -import path from 'path'; import { DevDataStore } from '@backstage/backend-dev-utils'; -import { mergeDatabaseConfig } from '../config'; -import { DatabaseConnector } from '../types'; import { LifecycleService, PluginMetadataService, } from '@backstage/backend-plugin-api'; +import { Config, ConfigReader } from '@backstage/config'; +import { InputError } from '@backstage/errors'; +import { JsonObject } from '@backstage/types'; +import { ensureDirSync } from 'fs-extra'; +import knexFactory, { Knex } from 'knex'; +import { merge, omit } from 'lodash'; +import path from 'path'; +import { Connector, DatabaseConnector } from '../types'; +import { mergeDatabaseConfig } from './mergeDatabaseConfig'; /** * Creates a knex SQLite3 database connection @@ -159,8 +162,250 @@ export function parseSqliteConnectionString( * * Exposes database connector functionality via an immutable object. */ -export const sqlite3Connector: DatabaseConnector = Object.freeze({ +export const sqliteConnector: DatabaseConnector = Object.freeze({ createClient: createSqliteDatabaseClient, createNameOverride: createSqliteNameOverride, parseConnectionString: parseSqliteConnectionString, }); + +/** + * Provides a config lookup path for a plugin's config block. + */ +function pluginPath(pluginId: string): string { + return `plugin.${pluginId}`; +} + +function normalizeConnection( + connection: Knex.StaticConnectionConfig | JsonObject | string | undefined, + client: string, +): Partial<Knex.StaticConnectionConfig> { + if (typeof connection === 'undefined' || connection === null) { + return {}; + } + + return typeof connection === 'string' || connection instanceof String + ? sqliteConnector.parseConnectionString(connection as string, client) + : connection; +} + +function createNameOverride( + client: string, + name: string, +): Partial<Knex.Config> { + try { + return sqliteConnector.createNameOverride(name); + } catch (e) { + throw new InputError( + `Unable to create database name override for '${client}' connector`, + e, + ); + } +} + +export class Sqlite3Connector implements Connector { + constructor(private readonly config: Config) {} + + async getClient( + pluginId: string, + deps?: { + lifecycle: LifecycleService; + pluginMetadata: PluginMetadataService; + }, + ): Promise<Knex> { + const pluginConfig = new ConfigReader( + this.getConfigForPlugin(pluginId) as JsonObject, + ); + + const pluginDivisionMode = this.getPluginDivisionModeConfig(); + if (pluginDivisionMode !== 'database') { + throw new Error( + `The SQLite driver does not suppoert plugin division mode '${pluginDivisionMode}'`, + ); + } + + const databaseClientOverrides = mergeDatabaseConfig( + {}, + this.getDatabaseOverrides(pluginId), + ); + + const client = sqliteConnector.createClient( + pluginConfig, + databaseClientOverrides, + deps, + ); + + return client; + } + + async dropDatabase(..._databaseNames: string[]): Promise<void> { + // do nothing + } + + /** + * Provides the canonical database name for a given plugin. + * + * This method provides the effective database name which is determined using global + * and plugin specific database config. If no explicit database name is configured + * and `pluginDivisionMode` is not `schema`, this method will provide a generated name + * which is the pluginId prefixed with 'backstage_plugin_'. If `pluginDivisionMode` is + * `schema`, it will fallback to using the default database for the knex instance. + * + * @param pluginId - Lookup the database name for given plugin + * @returns String representing the plugin's database name + */ + private getDatabaseName(pluginId: string): string | undefined { + const connection = this.getConnectionConfig(pluginId); + + const sqliteFilename: string | undefined = ( + connection as Knex.Sqlite3ConnectionConfig + ).filename; + + if (sqliteFilename === ':memory:') { + return sqliteFilename; + } + + const sqliteDirectory = + (connection as { directory?: string }).directory ?? '.'; + + return path.join(sqliteDirectory, sqliteFilename ?? `${pluginId}.sqlite`); + } + + /** + * Provides the client type which should be used for a given plugin. + * + * The client type is determined by plugin specific config if present. + * Otherwise the base client is used as the fallback. + * + * @param pluginId - Plugin to get the client type for + * @returns Object with client type returned as `client` and boolean + * representing whether or not the client was overridden as + * `overridden` + */ + private getClientType(pluginId: string): { + client: string; + overridden: boolean; + } { + const pluginClient = this.config.getOptionalString( + `${pluginPath(pluginId)}.client`, + ); + + const baseClient = this.config.getString('client'); + const client = pluginClient ?? baseClient; + return { + client, + overridden: client !== baseClient, + }; + } + + private getRoleConfig(pluginId: string): string | undefined { + return ( + this.config.getOptionalString(`${pluginPath(pluginId)}.role`) ?? + this.config.getOptionalString('role') + ); + } + + /** + * Provides the knexConfig which should be used for a given plugin. + * + * @param pluginId - Plugin to get the knexConfig for + * @returns The merged knexConfig value or undefined if it isn't specified + */ + private getAdditionalKnexConfig(pluginId: string): JsonObject | undefined { + const pluginConfig = this.config + .getOptionalConfig(`${pluginPath(pluginId)}.knexConfig`) + ?.get<JsonObject>(); + + const baseConfig = this.config + .getOptionalConfig('knexConfig') + ?.get<JsonObject>(); + + return merge(baseConfig, pluginConfig); + } + + private getPluginDivisionModeConfig(): string { + return this.config.getOptionalString('pluginDivisionMode') ?? 'database'; + } + + /** + * Provides a Knex connection plugin config by combining base and plugin + * config. + * + * This method provides a baseConfig for a plugin database connector. If the + * client type has not been overridden, the global connection config will be + * included with plugin specific config as the base. Values from the plugin + * connection take precedence over the base. Base database name is omitted for + * all supported databases excluding SQLite unless `pluginDivisionMode` is set + * to `schema`. + */ + private getConnectionConfig(pluginId: string): Knex.StaticConnectionConfig { + const { client, overridden } = this.getClientType(pluginId); + + let baseConnection = normalizeConnection( + this.config.get('connection'), + this.config.getString('client'), + ); + + if ( + client.includes('sqlite3') && + 'filename' in baseConnection && + baseConnection.filename !== ':memory:' + ) { + throw new Error( + '`connection.filename` is not supported for the base sqlite connection. Prefer `connection.directory` or provide a filename for the plugin connection instead.', + ); + } + + // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`. The + // `database` property from the base connection is omitted unless `pluginDivisionMode` + // is set to `schema`. SQLite3's `filename` property is an exception as this is used as a + // directory elsewhere so we preserve `filename`. + if (this.getPluginDivisionModeConfig() !== 'schema') { + baseConnection = omit(baseConnection, 'database'); + } + + // get and normalize optional plugin specific database connection + const connection = normalizeConnection( + this.config.getOptional(`${pluginPath(pluginId)}.connection`), + client, + ); + + return { + // include base connection if client type has not been overridden + ...(overridden ? {} : baseConnection), + ...connection, + } as Knex.StaticConnectionConfig; + } + + /** + * Provides a Knex database config for a given plugin. + * + * This method provides a Knex configuration object along with the plugin's + * client type. + * + * @param pluginId - The plugin that the database config should correspond with + */ + private getConfigForPlugin(pluginId: string): Knex.Config { + const { client } = this.getClientType(pluginId); + const role = this.getRoleConfig(pluginId); + + return { + ...this.getAdditionalKnexConfig(pluginId), + client, + connection: this.getConnectionConfig(pluginId), + ...(role && { role }), + }; + } + + /** + * Provides a partial `Knex.Config`â€ĸ database name override for a given plugin. + * + * @param pluginId - Target plugin to get database name override + * @returns Partial `Knex.Config` with database name override + */ + private getDatabaseOverrides(pluginId: string): Knex.Config { + const databaseName = this.getDatabaseName(pluginId); + return databaseName + ? createNameOverride(this.getClientType(pluginId).client, databaseName) + : {}; + } +} diff --git a/packages/backend-common/src/database/index.ts b/packages/backend-common/src/database/index.ts index 330429f0ef..a0a61ccdd0 100644 --- a/packages/backend-common/src/database/index.ts +++ b/packages/backend-common/src/database/index.ts @@ -14,17 +14,11 @@ * limitations under the License. */ -export * from './DatabaseManager'; - -/* - * Undocumented API surface from connection is being reduced for future deprecation. - * Avoid exporting additional symbols. - */ -export { - createDatabaseClient, - ensureDatabaseExists, - dropDatabase, -} from './connection'; +export { DatabaseManager, dropDatabase } from './DatabaseManager'; +export type { + DatabaseManagerOptions, + LegacyRootDatabaseService, +} from './DatabaseManager'; export type { PluginDatabaseManager } from './types'; export { isDatabaseConflictError } from './util'; diff --git a/packages/backend-common/src/database/types.ts b/packages/backend-common/src/database/types.ts index 3e9832aec5..a9cceaa1f9 100644 --- a/packages/backend-common/src/database/types.ts +++ b/packages/backend-common/src/database/types.ts @@ -24,11 +24,11 @@ import { Knex } from 'knex'; export type { DatabaseService as PluginDatabaseManager } from '@backstage/backend-plugin-api'; /** - * DatabaseConnector manages an underlying Knex database driver. + * Manages an underlying Knex database driver. */ export interface DatabaseConnector { /** - * createClient provides an instance of a knex database connector. + * Provides an instance of a knex database connector. */ createClient( dbConfig: Config, @@ -38,27 +38,29 @@ export interface DatabaseConnector { pluginMetadata: PluginMetadataService; }, ): Knex; + /** - * createNameOverride provides a partial knex config sufficient to override a - * database name. + * Provides a partial knex config sufficient to override a database name. */ createNameOverride(name: string): Partial<Knex.Config>; + /** - * createSchemaOverride provides a partial knex config sufficient to override a - * PostgreSQL schema name within utilizing the `searchPath` knex configuration. + * Provides a partial knex config sufficient to override a PostgreSQL schema + * name within utilizing the `searchPath` knex configuration. */ createSchemaOverride?(name: string): Partial<Knex.Config>; + /** - * parseConnectionString produces a knex connection config object representing - * a database connection string. + * Produces a knex connection config object representing a database connection + * string. */ parseConnectionString( connectionString: string, client?: string, ): Knex.StaticConnectionConfig; + /** - * ensureDatabaseExists performs a side-effect to ensure database names passed in are - * present. + * Performs a side-effect to ensure database names passed in are present. * * Calling this function on databases which already exist should do nothing. * Missing databases should be created if needed. @@ -69,8 +71,7 @@ export interface DatabaseConnector { ): Promise<void>; /** - * ensureSchemaExists performs a side-effect to ensure schema names passed in are - * present. + * Performs a side-effect to ensure schema names passed in are present. * * Calling this function on schemas which already exist should do nothing. * Missing schemas should be created if needed. @@ -80,5 +81,20 @@ export interface DatabaseConnector { ...schemas: Array<string> ): Promise<void>; + /** + * Deletes databases. + */ dropDatabase?(dbConfig: Config, ...databases: Array<string>): Promise<void>; } + +export interface Connector { + getClient( + pluginId: string, + deps?: { + lifecycle: LifecycleService; + pluginMetadata: PluginMetadataService; + }, + ): Promise<Knex>; + + dropDatabase(...databaseNames: string[]): Promise<void>; +} diff --git a/packages/backend-common/src/reading/HarnessUrlReader.test.ts b/packages/backend-common/src/reading/HarnessUrlReader.test.ts new file mode 100644 index 0000000000..bb09baa140 --- /dev/null +++ b/packages/backend-common/src/reading/HarnessUrlReader.test.ts @@ -0,0 +1,185 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { ConfigReader } from '@backstage/config'; +import { HarnessIntegration, readHarnessConfig } from '@backstage/integration'; +import { JsonObject } from '@backstage/types'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { getVoidLogger } from '../logging'; +import { UrlReaderPredicateTuple } from './types'; +import { DefaultReadTreeResponseFactory } from './tree'; +import getRawBody from 'raw-body'; +import { HarnessUrlReader } from './HarnessUrlReader'; + +const treeResponseFactory = DefaultReadTreeResponseFactory.create({ + config: new ConfigReader({}), +}); + +jest.mock('../scm', () => ({ + Git: { + fromAuth: () => ({ + clone: jest.fn(() => Promise.resolve({})), + }), + }, +})); + +const harnessProcessor = new HarnessUrlReader( + new HarnessIntegration( + readHarnessConfig( + new ConfigReader({ + host: 'app.harness.io', + token: 'p', + }), + ), + ), +); + +const createReader = (config: JsonObject): UrlReaderPredicateTuple[] => { + return HarnessUrlReader.factory({ + config: new ConfigReader(config), + logger: getVoidLogger(), + treeResponseFactory, + }); +}; +const responseBuffer = Buffer.from('Apache License'); +const harnessApiResponse = (content: any) => { + return content; +}; + +const handlers = [ + rest.get( + 'https://app.harness.io/gateway/code/api/v1/repos/accountId/orgName/projName/repoName/:path+/raw/all-apis.yaml', + (_req, res, ctx) => { + return res(ctx.status(500), ctx.json({ message: 'Error!!!' })); + }, + ), + rest.get( + 'https://app.harness.io/gateway/code/api/v1/repos/accountId/orgName/projName/repoName/:path+/raw/404error.yaml', + (_req, res, ctx) => { + return res(ctx.status(404), ctx.json({ message: 'File not found.' })); + }, + ), + rest.get( + 'https://app.harness.io/gateway/code/api/v1/repos/accountId/orgName/projName/repoName/:path+/raw/stream.TXT', + (_req, res, ctx) => { + return res( + ctx.status(200), + ctx.body(harnessApiResponse(responseBuffer.toString())), + ); + }, + ), + + rest.get( + 'https://app.harness.io/gateway/code/api/v1/repos/accountId/orgName/projName/repoName/:path+/raw/buffer.TXT', + (_req, res, ctx) => { + return res( + ctx.status(200), + ctx.body(harnessApiResponse(responseBuffer.toString())), + ); + }, + ), +]; + +describe('HarnessUrlReader', () => { + const worker = setupServer(...handlers); + setupRequestMockHandlers(worker); + beforeAll(() => worker.listen({ onUnhandledRequest: 'bypass' })); + afterAll(() => { + jest.clearAllMocks(); + }); + + describe('reader factory', () => { + it('creates a reader.', () => { + const readers = createReader({ + integrations: { + harness: [{ host: 'app.harness.io' }], + }, + }); + expect(readers).toHaveLength(1); + }); + + it('should not create a default entry.', () => { + const readers = createReader({ + integrations: {}, + }); + expect(readers).toHaveLength(0); + }); + }); + + describe('predicates', () => { + it('returns true for the configured host', () => { + const readers = createReader({ + integrations: { + harness: [{ host: 'app.harness.io' }], + }, + }); + const predicate = readers[0].predicate; + + expect(predicate(new URL('https://app.harness.io/path'))).toBe(true); + }); + + it('returns false for a different host.', () => { + const readers = createReader({ + integrations: { + harness: [{ host: 'app.harness.io' }], + }, + }); + const predicate = readers[0].predicate; + + expect(predicate(new URL('https://github.com/path'))).toBe(false); + }); + }); + + describe('readUrl part 1', () => { + it('should be able to read file contents as buffer', async () => { + const result = await harnessProcessor.readUrl( + 'https://app.harness.io/ng/account/accountId/module/code/orgs/orgName/projects/projName/repos/repoName/files/refMain/~/buffer.TXT', + ); + const buffer = await result.buffer(); + expect(buffer.toString()).toBe(responseBuffer.toString()); + }); + + it('should be able to read file contents as stream', async () => { + const result = await harnessProcessor.readUrl( + 'https://app.harness.io/ng/account/accountId/module/code/orgs/orgName/projects/projName/repos/repoName/files/refMain/~/stream.TXT', + ); + const fromStream = await getRawBody(result.stream!()); + expect(fromStream.toString()).toBe(responseBuffer.toString()); + }); + + it('should raise NotFoundError on 404.', async () => { + await expect( + harnessProcessor.readUrl( + 'https://app.harness.io/ng/account/accountId/module/code/orgs/orgName/projects/projName/repos/repoName/files/refMain/~/404error.yaml', + ), + ).rejects.toThrow( + 'https://app.harness.io/ng/account/accountId/module/code/orgs/orgName/projects/projName/repos/repoName/files/refMain/~/404error.yaml x https://app.harness.io/gateway/code/api/v1/repos/accountId/orgName/projName/repoName/+/raw/404error.yaml?routingId=accountId&git_ref=refMain, 404 Not Found', + ); + }); + + it('should throw an error on non 404 errors.', async () => { + await expect( + harnessProcessor.readUrl( + 'https://app.harness.io/ng/account/accountId/module/code/orgs/orgName/projects/projName/repos/repoName/files/refMain/~/all-apis.yaml', + ), + ).rejects.toThrow( + 'https://app.harness.io/ng/account/accountId/module/code/orgs/orgName/projects/projName/repos/repoName/files/refMain/~/all-apis.yaml x https://app.harness.io/gateway/code/api/v1/repos/accountId/orgName/projName/repoName/+/raw/all-apis.yaml?routingId=accountId&git_ref=refMain, 500 Internal Server Error', + ); + }); + }); +}); diff --git a/packages/backend-common/src/reading/HarnessUrlReader.ts b/packages/backend-common/src/reading/HarnessUrlReader.ts new file mode 100644 index 0000000000..09291c5176 --- /dev/null +++ b/packages/backend-common/src/reading/HarnessUrlReader.ts @@ -0,0 +1,125 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + getHarnessRequestOptions, + getHarnessFileContentsUrl, + HarnessIntegration, + ScmIntegrations, +} from '@backstage/integration'; +import { ReadUrlOptions, ReadUrlResponse } from './types'; +import { + ReaderFactory, + ReadTreeResponse, + SearchResponse, + UrlReader, +} from './types'; +import fetch, { Response } from 'node-fetch'; +import { ReadUrlResponseFactory } from './ReadUrlResponseFactory'; +import { + AuthenticationError, + NotFoundError, + NotModifiedError, +} from '@backstage/errors'; +import { Readable } from 'stream'; + +/** + * Implements a {@link @backstage/backend-plugin-api#UrlReaderService} for the Harness code v1 api. + * + * + * @public + */ +export class HarnessUrlReader implements UrlReader { + static factory: ReaderFactory = ({ config }) => { + return ScmIntegrations.fromConfig(config) + .harness.list() + .map(integration => { + const reader = new HarnessUrlReader(integration); + const predicate = (url: URL) => { + return url.host === integration.config.host; + }; + return { reader, predicate }; + }); + }; + + constructor(private readonly integration: HarnessIntegration) {} + + async read(url: string): Promise<Buffer> { + const response = await this.readUrl(url); + return response.buffer(); + } + + async readUrl( + url: string, + options?: ReadUrlOptions, + ): Promise<ReadUrlResponse> { + let response: Response; + const blobUrl = getHarnessFileContentsUrl(this.integration.config, url); + + try { + response = await fetch(blobUrl, { + method: 'GET', + ...getHarnessRequestOptions(this.integration.config), + signal: options?.signal as any, + }); + } catch (e) { + throw new Error(`Unable to read ${blobUrl}, ${e}`); + } + + if (response.ok) { + // Harness Code returns the raw content object + const jsonResponse = { data: response.body }; + if (jsonResponse) { + return ReadUrlResponseFactory.fromReadable( + Readable.from(jsonResponse.data), + { + etag: response.headers.get('ETag') ?? undefined, + }, + ); + } + + throw new Error(`Unknown json: ${jsonResponse}`); + } + + const message = `${url} x ${blobUrl}, ${response.status} ${response.statusText}`; + if (response.status === 404) { + throw new NotFoundError(message); + } + + if (response.status === 304) { + throw new NotModifiedError(); + } + + if (response.status === 403) { + throw new AuthenticationError(); + } + + throw new Error(message); + } + + readTree(): Promise<ReadTreeResponse> { + throw new Error('HarnessUrlReader readTree not implemented.'); + } + search(): Promise<SearchResponse> { + throw new Error('HarnessUrlReader search not implemented.'); + } + + toString() { + const { host } = this.integration.config; + return `harness{host=${host},authed=${Boolean( + this.integration.config.token || this.integration.config.apiKey, + )}}`; + } +} diff --git a/packages/backend-common/src/reading/UrlReaders.ts b/packages/backend-common/src/reading/UrlReaders.ts index e987eb9186..7aaaf120b4 100644 --- a/packages/backend-common/src/reading/UrlReaders.ts +++ b/packages/backend-common/src/reading/UrlReaders.ts @@ -31,6 +31,7 @@ import { GoogleGcsUrlReader } from './GoogleGcsUrlReader'; import { AwsS3UrlReader } from './AwsS3UrlReader'; import { GiteaUrlReader } from './GiteaUrlReader'; import { AwsCodeCommitUrlReader } from './AwsCodeCommitUrlReader'; +import { HarnessUrlReader } from './HarnessUrlReader'; /** * Creation options for {@link @backstage/backend-plugin-api#UrlReaderService}. @@ -61,7 +62,6 @@ export class UrlReaders { const treeResponseFactory = DefaultReadTreeResponseFactory.create({ config, }); - for (const factory of factories ?? []) { const tuples = factory({ config, logger: logger, treeResponseFactory }); @@ -94,6 +94,7 @@ export class UrlReaders { GiteaUrlReader.factory, GitlabUrlReader.factory, GoogleGcsUrlReader.factory, + HarnessUrlReader.factory, AwsS3UrlReader.factory, AwsCodeCommitUrlReader.factory, FetchUrlReader.factory, diff --git a/packages/backend-common/src/reading/index.ts b/packages/backend-common/src/reading/index.ts index 21e82da9b5..a99bb6dda1 100644 --- a/packages/backend-common/src/reading/index.ts +++ b/packages/backend-common/src/reading/index.ts @@ -22,6 +22,7 @@ export { GerritUrlReader } from './GerritUrlReader'; export { GithubUrlReader } from './GithubUrlReader'; export { GitlabUrlReader } from './GitlabUrlReader'; export { GiteaUrlReader } from './GiteaUrlReader'; +export { HarnessUrlReader } from './HarnessUrlReader'; export { AwsS3UrlReader } from './AwsS3UrlReader'; export { FetchUrlReader } from './FetchUrlReader'; export { ReadUrlResponseFactory } from './ReadUrlResponseFactory'; diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index a939df4eba..a9871d0aa5 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/backend-defaults +## 0.2.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.7.2-next.1 + - @backstage/backend-common@0.22.0-next.1 + +## 0.2.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.7.1-next.0 + - @backstage/backend-common@0.21.8-next.0 + ## 0.2.17 ### Patch Changes diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index 953449913e..0a9fb7de55 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-defaults", "description": "Backend defaults used by Backstage backend apps", - "version": "0.2.17", + "version": "0.2.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-dynamic-feature-service/CHANGELOG.md b/packages/backend-dynamic-feature-service/CHANGELOG.md index 8eee7f5dcb..f45e57bd9c 100644 --- a/packages/backend-dynamic-feature-service/CHANGELOG.md +++ b/packages/backend-dynamic-feature-service/CHANGELOG.md @@ -1,5 +1,52 @@ # @backstage/backend-dynamic-feature-service +## 0.2.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.7.2-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-catalog-backend@1.22.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-events-backend@0.3.5-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/cli-node@0.2.5 + - @backstage/config-loader@1.8.0 + - @backstage/backend-plugin-api@0.6.18-next.1 + - @backstage/plugin-app-node@0.1.18-next.1 + +## 0.2.10-next.0 + +### Patch Changes + +- b611fd0: Updates the `scanRoot` method in the `PluginScanner` class to specifically ignore the `lost+found` directory, which is a system-generated directory used for file recovery on Unix-like systems. Skipping this directory avoids unnecessary errors. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.22.0-next.0 + - @backstage/backend-app-api@0.7.1-next.0 + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.5 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.8.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-app-node@0.1.18-next.0 + - @backstage/plugin-events-backend@0.3.5-next.0 + - @backstage/plugin-events-node@0.3.4-next.0 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + - @backstage/plugin-search-common@1.2.11 + ## 0.2.9 ### Patch Changes diff --git a/packages/backend-dynamic-feature-service/package.json b/packages/backend-dynamic-feature-service/package.json index 593dbf155b..35d053d40a 100644 --- a/packages/backend-dynamic-feature-service/package.json +++ b/packages/backend-dynamic-feature-service/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-dynamic-feature-service", "description": "Backstage dynamic feature service", - "version": "0.2.9", + "version": "0.2.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-next/CHANGELOG.md b/packages/backend-next/CHANGELOG.md index 70dac3ac7f..5cc98ddb8a 100644 --- a/packages/backend-next/CHANGELOG.md +++ b/packages/backend-next/CHANGELOG.md @@ -1,5 +1,77 @@ # example-backend-next +## 0.0.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-unprocessed@0.4.5-next.1 + - @backstage/plugin-notifications-backend@0.2.1-next.1 + - @backstage/plugin-catalog-backend@1.22.0-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.16-next.1 + - @backstage/plugin-scaffolder-backend@1.22.5-next.1 + - @backstage/plugin-search-backend@1.5.8-next.1 + - @backstage/backend-defaults@0.2.18-next.1 + - @backstage/plugin-app-backend@0.3.66-next.1 + - @backstage/plugin-kubernetes-backend@0.17.1-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-auth-backend@0.22.5-next.1 + - @backstage/plugin-auth-backend-module-guest-provider@0.1.4-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-catalog-backend-module-openapi@0.1.36-next.1 + - @backstage/plugin-devtools-backend@0.3.4-next.1 + - @backstage/plugin-permission-backend@0.5.42-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/plugin-proxy-backend@0.4.16-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.2.8-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.24-next.1 + - @backstage/plugin-search-backend-module-explore@0.1.24-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.1.23-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/plugin-signals-backend@0.1.4-next.1 + - @backstage/plugin-techdocs-backend@1.10.5-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.1.15-next.1 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.1.15-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.2.1-next.1 + +## 0.0.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-github@0.2.8-next.0 + - @backstage/plugin-catalog-backend@1.22.0-next.0 + - @backstage/plugin-scaffolder-backend@1.22.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/plugin-search-backend@1.5.8-next.0 + - @backstage/plugin-search-backend-module-catalog@0.1.23-next.0 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.2.1-next.0 + - @backstage/plugin-auth-backend-module-guest-provider@0.1.4-next.0 + - @backstage/plugin-search-backend-module-explore@0.1.23-next.0 + - @backstage/plugin-auth-backend@0.22.5-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/plugin-notifications-backend@0.2.1-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-backend-module-openapi@0.1.36-next.0 + - @backstage/backend-defaults@0.2.18-next.0 + - @backstage/plugin-app-backend@0.3.66-next.0 + - @backstage/plugin-kubernetes-backend@0.17.1-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.16-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.4.5-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.1.23-next.0 + - @backstage/plugin-techdocs-backend@1.10.5-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.1.15-next.0 + - @backstage/plugin-devtools-backend@0.3.4-next.0 + - @backstage/plugin-permission-backend@0.5.42-next.0 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.1.15-next.0 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + - @backstage/plugin-proxy-backend@0.4.16-next.0 + - @backstage/plugin-signals-backend@0.1.4-next.0 + ## 0.0.25 ### Patch Changes diff --git a/packages/backend-next/package.json b/packages/backend-next/package.json index 9de8bb1788..f5c61bd8ff 100644 --- a/packages/backend-next/package.json +++ b/packages/backend-next/package.json @@ -1,6 +1,6 @@ { "name": "example-backend-next", - "version": "0.0.25", + "version": "0.0.26-next.1", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/backend-openapi-utils/CHANGELOG.md b/packages/backend-openapi-utils/CHANGELOG.md index 60df1440fa..3978b5dfd0 100644 --- a/packages/backend-openapi-utils/CHANGELOG.md +++ b/packages/backend-openapi-utils/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/backend-openapi-utils +## 0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/errors@1.2.4 + ## 0.1.10 ### Patch Changes diff --git a/packages/backend-openapi-utils/package.json b/packages/backend-openapi-utils/package.json index 65806fa949..f77626001d 100644 --- a/packages/backend-openapi-utils/package.json +++ b/packages/backend-openapi-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-openapi-utils", "description": "OpenAPI typescript support.", - "version": "0.1.10", + "version": "0.1.11-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/backend-plugin-api/CHANGELOG.md b/packages/backend-plugin-api/CHANGELOG.md index 168970dc6c..e6a0d5c278 100644 --- a/packages/backend-plugin-api/CHANGELOG.md +++ b/packages/backend-plugin-api/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/backend-plugin-api +## 0.6.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + +## 0.6.18-next.0 + +### Patch Changes + +- 1fedf0c: Removed explicit `toString()` method from `ServiceRef` type. +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + ## 0.6.17 ### Patch Changes diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index 3a5b8bdb9d..fff99c0370 100644 --- a/packages/backend-plugin-api/package.json +++ b/packages/backend-plugin-api/package.json @@ -1,20 +1,30 @@ { "name": "@backstage/backend-plugin-api", + "version": "0.6.18-next.1", "description": "Core API used by Backstage backend plugins", - "version": "0.6.17", - "main": "src/index.ts", - "types": "src/index.ts", - "publishConfig": { - "access": "public" - }, "backstage": { "role": "node-library" }, + "publishConfig": { + "access": "public" + }, + "keywords": [ + "backstage" + ], + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/backend-plugin-api" + }, + "license": "Apache-2.0", "exports": { ".": "./src/index.ts", "./alpha": "./src/alpha.ts", "./package.json": "./package.json" }, + "main": "src/index.ts", + "types": "src/index.ts", "typesVersions": { "*": { "alpha": [ @@ -25,24 +35,17 @@ ] } }, - "homepage": "https://backstage.io", - "repository": { - "type": "git", - "url": "https://github.com/backstage/backstage", - "directory": "packages/backend-plugin-api" - }, - "keywords": [ - "backstage" + "files": [ + "dist" ], - "license": "Apache-2.0", "scripts": { "build": "backstage-cli package build", + "clean": "backstage-cli package clean", "lint": "backstage-cli package lint", - "test": "backstage-cli package test", "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", - "clean": "backstage-cli package clean", - "start": "backstage-cli package start" + "start": "backstage-cli package start", + "test": "backstage-cli package test" }, "dependencies": { "@backstage/backend-tasks": "workspace:^", @@ -56,9 +59,5 @@ }, "devDependencies": { "@backstage/cli": "workspace:^" - }, - "files": [ - "dist", - "alpha" - ] + } } diff --git a/packages/backend-tasks/CHANGELOG.md b/packages/backend-tasks/CHANGELOG.md index 7d7cb842c4..f90d8ff7ef 100644 --- a/packages/backend-tasks/CHANGELOG.md +++ b/packages/backend-tasks/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/backend-tasks +## 0.5.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.5.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.5.22 ### Patch Changes diff --git a/packages/backend-tasks/package.json b/packages/backend-tasks/package.json index a996207ece..a7f64b9db1 100644 --- a/packages/backend-tasks/package.json +++ b/packages/backend-tasks/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-tasks", "description": "Common distributed task management library for Backstage backends", - "version": "0.5.22", + "version": "0.5.23-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 77abe491c2..6602f4cbe8 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/backend-test-utils +## 0.3.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.7.2-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.7.1-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.3.7 ### Patch Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 6035e47a4b..d88447efbb 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-test-utils", - "version": "0.3.7", + "version": "0.3.8-next.1", "description": "Test helpers library for Backstage backends", "backstage": { "role": "node-library" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index b0bf1a8b91..06aba6c5ca 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,84 @@ # example-backend +## 0.2.98-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-unprocessed@0.4.5-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-catalog-backend@1.22.0-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.16-next.1 + - @backstage/plugin-scaffolder-backend@1.22.5-next.1 + - example-app@0.2.97-next.1 + - @backstage/plugin-search-backend@1.5.8-next.1 + - @backstage/plugin-app-backend@0.3.66-next.1 + - @backstage/plugin-kubernetes-backend@0.17.1-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-auth-backend@0.22.5-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-devtools-backend@0.3.4-next.1 + - @backstage/plugin-events-backend@0.3.5-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-permission-backend@0.5.42-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/plugin-proxy-backend@0.4.16-next.1 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.19-next.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.3.4-next.1 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.35-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.24-next.1 + - @backstage/plugin-search-backend-module-elasticsearch@1.4.1-next.1 + - @backstage/plugin-search-backend-module-explore@0.1.24-next.1 + - @backstage/plugin-search-backend-module-pg@0.5.27-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.1.23-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/plugin-signals-backend@0.1.4-next.1 + - @backstage/plugin-signals-node@0.1.4-next.1 + - @backstage/plugin-techdocs-backend@1.10.5-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + +## 0.2.98-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.22.0-next.0 + - @backstage/plugin-scaffolder-backend@1.22.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/plugin-search-backend@1.5.8-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.3.4-next.0 + - @backstage/plugin-search-backend-module-catalog@0.1.23-next.0 + - @backstage/plugin-search-backend-module-explore@0.1.23-next.0 + - @backstage/plugin-auth-backend@0.22.5-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - example-app@0.2.97-next.0 + - @backstage/plugin-app-backend@0.3.66-next.0 + - @backstage/plugin-kubernetes-backend@0.17.1-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.16-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.4.5-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.1.23-next.0 + - @backstage/plugin-techdocs-backend@1.10.5-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.10.0 + - @backstage/plugin-devtools-backend@0.3.4-next.0 + - @backstage/plugin-events-backend@0.3.5-next.0 + - @backstage/plugin-events-node@0.3.4-next.0 + - @backstage/plugin-permission-backend@0.5.42-next.0 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + - @backstage/plugin-proxy-backend@0.4.16-next.0 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.19-next.0 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.35-next.0 + - @backstage/plugin-search-backend-module-elasticsearch@1.4.1-next.0 + - @backstage/plugin-search-backend-module-pg@0.5.27-next.0 + - @backstage/plugin-signals-backend@0.1.4-next.0 + - @backstage/plugin-signals-node@0.1.4-next.0 + ## 0.2.97 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index ee2d70a09a..ea37121492 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.97", + "version": "0.2.98-next.1", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index b210a4c653..ef3ca6588e 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/catalog-client +## 1.6.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/errors@1.2.4 + ## 1.6.4 ### Patch Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index 1a942e7182..39d8227fa5 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-client", - "version": "1.6.4", + "version": "1.6.5-next.0", "description": "An isomorphic client for the catalog backend", "backstage": { "role": "common-library" diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index 4a3633d5de..c04ac72136 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/catalog-model +## 1.5.0-next.0 + +### Minor Changes + +- 79025f3: Introduce a domain attribute to the domain entity to allow a hierarchy of domains to exist. + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 1.4.5 ### Patch Changes diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 2dd4001d00..5622a20941 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-model", - "version": "1.4.5", + "version": "1.5.0-next.0", "description": "Types and validators that help describe the model of a Backstage Catalog", "backstage": { "role": "common-library" diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 6a466591b2..6c37772f73 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/cli +## 0.26.5-next.0 + +### Patch Changes + +- fcd3462: Both the target and types library have been bumped from ES2021 to ES2022 in `@backstage/cli/config/tsconfig.json`. +- 0cc5ed3: Add support for `versions:migrate` to do code changes. Can be skipped with `--no-code-changes` +- f97ad04: Add preserveModules to rollup, which allows better async loading and tree-shaking in webpack +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.5 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.8.0 + - @backstage/errors@1.2.4 + - @backstage/eslint-plugin@0.1.7 + - @backstage/integration@1.10.0 + - @backstage/release-manifests@0.0.11 + - @backstage/types@1.1.1 + ## 0.26.3 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 537aec421d..c8baae4d4d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,34 +1,46 @@ { "name": "@backstage/cli", + "version": "0.26.5-next.0", "description": "CLI for developing Backstage plugins and apps", - "version": "0.26.3", - "publishConfig": { - "access": "public" - }, "backstage": { "role": "cli" }, + "publishConfig": { + "access": "public" + }, + "keywords": [ + "backstage" + ], "homepage": "https://backstage.io", "repository": { "type": "git", "url": "https://github.com/backstage/backstage", "directory": "packages/cli" }, - "keywords": [ - "backstage" - ], "license": "Apache-2.0", "main": "dist/index.cjs.js", - "scripts": { - "build": "backstage-cli package build", - "lint": "backstage-cli package lint", - "test": "backstage-cli package test", - "clean": "backstage-cli package clean", - "start": "nodemon --" - }, "bin": { "backstage-cli": "bin/backstage-cli" }, + "files": [ + "asset-types", + "templates", + "config", + "bin", + "dist/**/*.js" + ], + "scripts": { + "build": "backstage-cli package build", + "clean": "backstage-cli package clean", + "lint": "backstage-cli package lint", + "start": "nodemon --", + "test": "backstage-cli package test" + }, + "nodemonConfig": { + "exec": "bin/backstage-cli", + "ext": "ts", + "watch": "./src" + }, "dependencies": { "@backstage/catalog-model": "workspace:^", "@backstage/cli-common": "workspace:^", @@ -197,18 +209,6 @@ "optional": true } }, - "files": [ - "asset-types", - "templates", - "config", - "bin", - "dist/**/*.js" - ], - "nodemonConfig": { - "watch": "./src", - "exec": "bin/backstage-cli", - "ext": "ts" - }, "configSchema": { "$schema": "https://backstage.io/schema/config-v1", "title": "@backstage/cli", @@ -248,6 +248,16 @@ "type": "string", "visibility": "frontend", "description": "site for Datadog RUM events" + }, + "sessionSampleRate": { + "type": "number", + "visibility": "frontend", + "description": "sample rate of Datadog RUM events" + }, + "sessionReplaySampleRate": { + "type": "number", + "visibility": "frontend", + "description": "sample rate of session replays based upon already sampled Datadog RUM events" } }, "required": [ diff --git a/packages/cli/src/commands/repo/fix.ts b/packages/cli/src/commands/repo/fix.ts index b975203a73..049f5a8416 100644 --- a/packages/cli/src/commands/repo/fix.ts +++ b/packages/cli/src/commands/repo/fix.ts @@ -22,11 +22,7 @@ import { } from '@backstage/cli-node'; import { OptionValues } from 'commander'; import fs from 'fs-extra'; -import { - resolve as resolvePath, - join as joinPath, - relative as relativePath, -} from 'path'; +import { resolve as resolvePath, posix, relative as relativePath } from 'path'; import { paths } from '../../lib/paths'; /** @@ -205,7 +201,7 @@ export function createRepositoryFieldFixer() { const rootDir = rootRepoField.directory || ''; return (pkg: FixablePackage) => { - const expectedPath = joinPath( + const expectedPath = posix.join( rootDir, relativePath(paths.targetRoot, pkg.dir), ); diff --git a/packages/cli/src/commands/versions/bump.test.ts b/packages/cli/src/commands/versions/bump.test.ts index c8174639a6..0dfe2f61c3 100644 --- a/packages/cli/src/commands/versions/bump.test.ts +++ b/packages/cli/src/commands/versions/bump.test.ts @@ -894,30 +894,19 @@ describe('bump', () => { newVersions: [], newRanges: [ { - name: 'first-duplicate', - oldRange: 'first-duplicate', - newRange: 'first-duplicate', - oldVersion: '1.0.0', - newVersion: '2.0.0', - }, - { - name: 'second-duplicate', - oldRange: 'second-duplicate', - newRange: 'second-duplicate', - oldVersion: '1.0.0', - newVersion: '2.0.0', - }, - { - name: 'third-duplicate', - oldRange: 'third-duplicate', - newRange: 'third-duplicate', + name: '@backstage/backend-app-api', + oldRange: '^1.0.0', + newRange: '^2.0.0', oldVersion: '1.0.0', newVersion: '2.0.0', }, ], }); mockDir.setContent({ - 'yarn.lock': lockfileMock, + 'yarn.lock': `${HEADER} +"@backstage/backend-app-api@^1.0.0": + version "1.0.0" +`, 'package.json': JSON.stringify({ workspaces: { packages: ['packages/*'], @@ -928,16 +917,7 @@ describe('bump', () => { 'package.json': JSON.stringify({ name: 'a', dependencies: { - '@backstage/core': '^1.0.5', - }, - }), - }, - b: { - 'package.json': JSON.stringify({ - name: 'b', - dependencies: { - '@backstage/core': '^1.0.3', - '@backstage/theme': '^1.0.0', + '@backstage/backend-app-api': '^1.0.0', }, }), }, @@ -952,7 +932,12 @@ describe('bump', () => { res( ctx.status(200), ctx.json({ - packages: [], + packages: [ + { + name: '@backstage/backend-app-api', + version: '2.0.0', + }, + ], }), ), ), @@ -962,24 +947,20 @@ describe('bump', () => { }); expectLogsToMatch(logs, [ 'Using default pattern glob @backstage/*', - 'Checking for updates of @backstage/core', - 'Checking for updates of @backstage/theme', - 'Checking for updates of @backstage/core-api', + 'Checking for updates of @backstage/backend-app-api', + 'Checking for updates of @backstage/backend-app-api', 'Some packages are outdated, updating', - 'unlocking @backstage/core@^1.0.3 ~> 1.0.6', - 'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7', - 'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7', - 'bumping @backstage/core in a to ^1.0.6', - 'bumping @backstage/core in b to ^1.0.6', - 'bumping @backstage/theme in b to ^2.0.0', + 'bumping @backstage/backend-app-api in a to ^2.0.0', 'Running yarn install to install new versions', 'Checking for moved packages to the @backstage-community namespace...', 'âš ī¸ The following packages may have breaking changes:', - ' @backstage/theme : 1.0.0 ~> 2.0.0', - ' https://github.com/backstage/backstage/blob/master/packages/theme/CHANGELOG.md', + ' @backstage/backend-app-api : 1.0.0 ~> 2.0.0', + ' https://github.com/backstage/backstage/blob/master/packages/backend-app-api/CHANGELOG.md', 'Version bump complete!', - 'The following packages have duplicates but have been allowed:', - 'first-duplicate, second-duplicate, third-duplicate', + ' âš ī¸ Warning! âš ī¸', + ' The below package(s) have incompatible duplicate installations, likely due to a bad dependency in a plugin.', + ' You can investigate this by running `yarn why <package-name>`, and report the issue to the plugin maintainers.', + ' @backstage/backend-app-api', ]); }); }); diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts index aad5a7831f..1544240704 100644 --- a/packages/cli/src/commands/versions/bump.ts +++ b/packages/cli/src/commands/versions/bump.ts @@ -331,24 +331,22 @@ export default async (opts: OptionValues) => { forbiddenDuplicatesFilter(name), ); if (forbiddenNewRanges.length > 0) { - throw new Error( - `Version bump failed for ${forbiddenNewRanges - .map(i => i.name) - .join(', ')}`, - ); - } - - const allowedDuplicates = result.newRanges.filter( - ({ name }) => !forbiddenDuplicatesFilter(name), - ); - - if (allowedDuplicates.length > 0) { + console.log(chalk.yellow(' âš ī¸ Warning! âš ī¸')); + console.log(); console.log( chalk.yellow( - 'The following packages have duplicates but have been allowed:', + ' The below package(s) have incompatible duplicate installations, likely due to a bad dependency in a plugin.', ), ); - console.log(chalk.yellow(allowedDuplicates.map(i => i.name).join(', '))); + console.log( + chalk.yellow( + ' You can investigate this by running `yarn why <package-name>`, and report the issue to the plugin maintainers.', + ), + ); + console.log(); + for (const { name } of forbiddenNewRanges) { + console.log(chalk.yellow(` ${name}`)); + } } }; diff --git a/packages/cli/src/commands/versions/lint.ts b/packages/cli/src/commands/versions/lint.ts index 9466fd0ab7..bfb2a752a0 100644 --- a/packages/cli/src/commands/versions/lint.ts +++ b/packages/cli/src/commands/versions/lint.ts @@ -27,10 +27,7 @@ export const includedFilter = (name: string) => INCLUDED.some(pattern => pattern.test(name)); // Packages that are not allowed to have any duplicates -const FORBID_DUPLICATES = [ - /^@backstage\/core-app-api$/, - /^@backstage\/plugin-/, -]; +const FORBID_DUPLICATES = [/^@backstage\/\w+-app-api$/, /^@backstage\/plugin-/]; // There are some packages that ARE explicitly allowed to have duplicates since // they handle that appropriately. This takes precedence over FORBID_DUPLICATES diff --git a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs index 1fe424ab9b..1e746ff39a 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs +++ b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs @@ -8,7 +8,7 @@ describe('ExampleFetchComponent', () => { // Wait for the table to render const table = await screen.findByRole('table'); - const nationality = screen.getAllByText('GB') + const nationality = screen.getAllByText('GB'); // Assert that the table contains the expected user data expect(table).toBeInTheDocument(); expect(screen.getByAltText('Carolyn')).toBeInTheDocument(); diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index 6f7c38735a..3e3f824816 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/core-compat-api +## 0.2.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.5-next.1 + +## 0.2.5-next.0 + +### Patch Changes + +- 5d99272: Update local development dependencies. +- Updated dependencies + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/version-bridge@1.0.8 + ## 0.2.4 ### Patch Changes diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index 6dd2115b9c..fbea128a68 100644 --- a/packages/core-compat-api/package.json +++ b/packages/core-compat-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-compat-api", - "version": "0.2.4", + "version": "0.2.5-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index da04ef9e2b..2c6d4ea490 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/core-components +## 0.14.6-next.1 + +### Patch Changes + +- 5223c4c: Fixed an internal circular import that broke Jest mocks. +- 5b7b49b: The `SignInPage` guest provider will now fall back to legacy guest auth if the backend request fails, allowing guest auth without a running backend. + +## 0.14.5-next.0 + +### Patch Changes + +- 71e4229: Added optional `initialShowDropDown` prop to `SidebarSubmenuItem` to internally manage the initial display state of the dropdown items. +- Updated dependencies + - @backstage/theme@0.5.4-next.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/version-bridge@1.0.8 + ## 0.14.4 ### Patch Changes diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 7fd706db43..09870afb9b 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -448,6 +448,7 @@ export type GaugeProps = { inverse?: boolean; unit?: string; max?: number; + size?: 'normal' | 'small'; description?: ReactNode; getColor?: GaugePropsGetColor; }; @@ -1128,6 +1129,7 @@ export type SidebarSubmenuItemProps = { icon?: IconComponent; dropdownItems?: SidebarSubmenuItemDropdownItem[]; exact?: boolean; + initialShowDropdown?: boolean; }; // @public diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 9f632bcc7f..87f51dff26 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-components", "description": "Core components used by Backstage plugins and apps", - "version": "0.14.4", + "version": "0.14.6-next.1", "publishConfig": { "access": "public" }, diff --git a/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.stories.tsx b/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.stories.tsx new file mode 100644 index 0000000000..e472ab5599 --- /dev/null +++ b/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.stories.tsx @@ -0,0 +1,48 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { HeaderIconLinkRow } from '../HeaderIconLinkRow'; +import { IconLinkVerticalProps } from './IconLinkVertical'; + +type Props = { + links: IconLinkVerticalProps[]; +}; + +export default { + title: 'Data Display/HeaderIconLinkRow', + component: HeaderIconLinkRow, +}; + +export const Default = (args: Props) => <HeaderIconLinkRow {...args} />; +Default.args = { + links: [ + { + color: 'primary', + disabled: false, + href: 'https://google.com', + label: 'primary', + title: 'title', + }, + { + color: 'secondary', + disabled: false, + href: 'https://google.com', + label: 'secondary', + title: 'title-2', + }, + ], +}; diff --git a/packages/core-components/src/components/ProgressBars/Gauge.tsx b/packages/core-components/src/components/ProgressBars/Gauge.tsx index 088e369e62..e72172fd59 100644 --- a/packages/core-components/src/components/ProgressBars/Gauge.tsx +++ b/packages/core-components/src/components/ProgressBars/Gauge.tsx @@ -19,6 +19,7 @@ import { makeStyles, useTheme } from '@material-ui/core/styles'; import { Circle } from 'rc-progress'; import React, { ReactNode, useEffect, useState } from 'react'; import Box from '@material-ui/core/Box'; +import classNames from 'classnames'; /** @public */ export type GaugeClassKey = @@ -43,6 +44,9 @@ const useStyles = makeStyles( fontWeight: theme.typography.fontWeightBold, color: theme.palette.textContrast, }, + overlaySmall: { + fontSize: theme.typography.pxToRem(25), + }, description: { fontSize: '100%', top: '50%', @@ -68,6 +72,7 @@ export type GaugeProps = { inverse?: boolean; unit?: string; max?: number; + size?: 'normal' | 'small'; description?: ReactNode; getColor?: GaugePropsGetColor; }; @@ -121,7 +126,7 @@ export const getProgressColor: GaugePropsGetColor = ({ export function Gauge(props: GaugeProps) { const [hoverRef, setHoverRef] = useState<HTMLDivElement | null>(null); - const { getColor = getProgressColor } = props; + const { getColor = getProgressColor, size = 'normal' } = props; const classes = useStyles(props); const { palette } = useTheme(); const { value, fractional, inverse, unit, max, description } = { @@ -165,7 +170,11 @@ export function Gauge(props: GaugeProps) { {description && isHovering ? ( <Box className={classes.description}>{description}</Box> ) : ( - <Box className={classes.overlay}> + <Box + className={classNames(classes.overlay, { + [classes.overlaySmall]: size === 'small', + })} + > {isNaN(value) ? 'N/A' : `${asActual}${unit}`} </Box> )} diff --git a/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx b/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx index a0b6faea60..f7e2eb69c6 100644 --- a/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx +++ b/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx @@ -175,6 +175,91 @@ export const InfoMessage = () => ( </Wrapper> ); +export const AlignedBottom = () => ( + <Wrapper> + <Grid item> + <GaugeCard + variant="fullHeight" + alignGauge="bottom" + title="Progress" + subheader="With a subheader" + progress={0.3} + /> + </Grid> + <Grid item> + <GaugeCard + variant="fullHeight" + alignGauge="bottom" + title="Progress" + subheader="With a subheader" + progress={0.57} + /> + </Grid> + <Grid item> + <GaugeCard + variant="fullHeight" + alignGauge="bottom" + title="Progress with longer title" + subheader="With a subheader" + progress={0.89} + /> + </Grid> + <Grid item> + <GaugeCard + variant="fullHeight" + alignGauge="bottom" + title="Progress" + subheader="With a subheader" + inverse + progress={0.2} + /> + </Grid> + </Wrapper> +); + +export const Small = () => ( + <Wrapper> + <Grid item> + <GaugeCard + variant="fullHeight" + alignGauge="bottom" + size="small" + title="Progress" + progress={0.3} + /> + </Grid> + <Grid item> + <GaugeCard + variant="fullHeight" + alignGauge="bottom" + size="small" + title="Progress" + subheader="With a subheader" + progress={0.57} + /> + </Grid> + <Grid item> + <GaugeCard + variant="fullHeight" + alignGauge="bottom" + size="small" + title="Progress, longer title" + progress={0.89} + /> + </Grid> + <Grid item> + <GaugeCard + variant="fullHeight" + alignGauge="bottom" + size="small" + title="Progress" + inverse + progress={0.2} + /> + </Grid> + </Wrapper> +); + export const HoverMessage = () => ( <Wrapper> <Grid item> diff --git a/packages/core-components/src/components/ProgressBars/GaugeCard.tsx b/packages/core-components/src/components/ProgressBars/GaugeCard.tsx index 3442395380..9f6de8eafe 100644 --- a/packages/core-components/src/components/ProgressBars/GaugeCard.tsx +++ b/packages/core-components/src/components/ProgressBars/GaugeCard.tsx @@ -27,6 +27,8 @@ type Props = { variant?: InfoCardVariants; /** Progress in % specified as decimal, e.g. "0.23" */ progress: number; + alignGauge?: 'normal' | 'bottom'; + size?: 'normal' | 'small'; description?: ReactNode; icon?: ReactNode; inverse?: boolean; @@ -43,6 +45,10 @@ const useStyles = makeStyles( height: '100%', width: 250, }, + rootSmall: { + height: '100%', + width: 160, + }, }, { name: 'BackstageGaugeCard' }, ); @@ -64,6 +70,8 @@ export function GaugeCard(props: Props) { description, icon, variant, + alignGauge = 'normal', + size = 'normal', getColor, } = props; @@ -75,15 +83,22 @@ export function GaugeCard(props: Props) { }; return ( - <Box className={classes.root}> + <Box className={size === 'small' ? classes.rootSmall : classes.root}> <InfoCard title={title} subheader={subheader} deepLink={deepLink} variant={variant} + alignContent={alignGauge} icon={icon} + titleTypographyProps={{ + ...(size === 'small' ? { variant: 'subtitle2' } : undefined), + }} + subheaderTypographyProps={{ + ...(size === 'small' ? { variant: 'body2' } : undefined), + }} > - <Gauge {...gaugeProps} /> + <Gauge {...gaugeProps} size={size} /> </InfoCard> </Box> ); diff --git a/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.stories.tsx b/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.stories.tsx new file mode 100644 index 0000000000..8d03e7359f --- /dev/null +++ b/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.stories.tsx @@ -0,0 +1,41 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { ResponseErrorPanel } from '../ResponseErrorPanel'; +import { ErrorPanelProps } from '../ErrorPanel'; + +export default { + title: 'Data Display/ResponseErrorPanel', + component: ResponseErrorPanel, +}; + +export const Default = (args: ErrorPanelProps) => ( + <ResponseErrorPanel {...args} /> +); +Default.args = { + error: new Error('Error message from error object'), + defaultExpanded: false, +}; + +export const WithTitle = (args: ErrorPanelProps) => ( + <ResponseErrorPanel {...args} /> +); +WithTitle.args = { + error: new Error('test'), + defaultExpanded: false, + title: 'Title prop is passed', +}; diff --git a/packages/core-components/src/layout/BottomLink/BottomLink.stories.tsx b/packages/core-components/src/layout/BottomLink/BottomLink.stories.tsx new file mode 100644 index 0000000000..e05af86993 --- /dev/null +++ b/packages/core-components/src/layout/BottomLink/BottomLink.stories.tsx @@ -0,0 +1,31 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { BottomLink } from '../BottomLink'; + +export default { + title: 'Layout/BottomLink', + component: BottomLink, +}; + +export const Default = (args: { link: string; title: string }) => ( + <BottomLink {...args} /> +); +Default.args = { + link: 'https://google.com', + title: 'This is bottom link', +}; diff --git a/packages/core-components/src/layout/ContentHeader/ContentHeader.stories.tsx b/packages/core-components/src/layout/ContentHeader/ContentHeader.stories.tsx new file mode 100644 index 0000000000..27df00e72c --- /dev/null +++ b/packages/core-components/src/layout/ContentHeader/ContentHeader.stories.tsx @@ -0,0 +1,73 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { ReactNode } from 'react'; +import { ContentHeader } from '../ContentHeader'; + +export default { + title: 'Layout/ContentHeader', + component: ContentHeader, +}; + +type ContentHeaderProps = { + title?: string; + titleComponent?: ReactNode; + description?: string; + textAlign?: 'left' | 'right' | 'center'; +}; + +export const Default = (args: ContentHeaderProps) => ( + <ContentHeader {...args}> + <div>Child of Content Header</div> + </ContentHeader> +); +Default.args = { + title: 'This is Content Header default aligned', + description: 'This is description', +}; + +export const Left = (args: ContentHeaderProps) => ( + <ContentHeader {...args}> + <div>Child of Content Header</div> + </ContentHeader> +); +Left.args = { + title: 'This is Content Header left aligned', + description: 'This is description', + textAlign: 'left', +}; + +export const Right = (args: ContentHeaderProps) => ( + <ContentHeader {...args}> + <div>Child of Content Header</div> + </ContentHeader> +); +Right.args = { + title: 'This is Content Header right aligned', + description: 'This is description', + textAlign: 'right', +}; + +export const Center = (args: ContentHeaderProps) => ( + <ContentHeader {...args}> + <div>Child of Content Header</div> + </ContentHeader> +); +Center.args = { + title: 'This is Content Header center aligned', + description: 'This is description', + textAlign: 'center', +}; diff --git a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx index 852622c741..9ee9c5525f 100644 --- a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx +++ b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx @@ -18,7 +18,7 @@ import { makeStyles } from '@material-ui/core/styles'; import TabUI, { TabProps } from '@material-ui/core/Tab'; import Tabs from '@material-ui/core/Tabs'; import React, { useCallback, useEffect, useState } from 'react'; -import { Link } from '@backstage/core-components'; +import { Link } from '../../components/Link'; // TODO(blam): Remove this implementation when the Tabs are ready // This is just a temporary solution to implementing tabs for now diff --git a/packages/core-components/src/layout/InfoCard/InfoCard.tsx b/packages/core-components/src/layout/InfoCard/InfoCard.tsx index c80e71b609..a08a816c2e 100644 --- a/packages/core-components/src/layout/InfoCard/InfoCard.tsx +++ b/packages/core-components/src/layout/InfoCard/InfoCard.tsx @@ -43,6 +43,10 @@ const useStyles = makeStyles( paddingBottom: 0, }, }, + contentAlignBottom: { + display: 'flex', + alignItems: 'self-end', + }, header: { padding: theme.spacing(2, 2, 2, 2.5), }, @@ -138,6 +142,7 @@ export type Props = { slackChannel?: string; errorBoundaryProps?: ErrorBoundaryProps; variant?: InfoCardVariants; + alignContent?: 'normal' | 'bottom'; children?: ReactNode; headerStyle?: object; headerProps?: CardHeaderProps; @@ -150,6 +155,7 @@ export type Props = { className?: string; noPadding?: boolean; titleTypographyProps?: object; + subheaderTypographyProps?: object; }; /** @@ -167,6 +173,7 @@ export function InfoCard(props: Props): JSX.Element { slackChannel, errorBoundaryProps, variant, + alignContent = 'normal', children, headerStyle, headerProps, @@ -179,6 +186,7 @@ export function InfoCard(props: Props): JSX.Element { className, noPadding, titleTypographyProps, + subheaderTypographyProps, } = props; const classes = useStyles(); /** @@ -209,10 +217,7 @@ export function InfoCard(props: Props): JSX.Element { } return ( - <div - className={classes.headerSubheader} - data-testid="info-card-subheader" - > + <div data-testid="info-card-subheader"> {subheader && <div className={classes.subheader}>{subheader}</div>} {icon} </div> @@ -228,7 +233,7 @@ export function InfoCard(props: Props): JSX.Element { {title && ( <CardHeader classes={{ - root: classes.header, + root: classNames(classes.header), title: classes.headerTitle, subheader: classes.headerSubheader, avatar: classes.headerAvatar, @@ -240,6 +245,7 @@ export function InfoCard(props: Props): JSX.Element { action={action} style={{ ...headerStyle }} titleTypographyProps={titleTypographyProps} + subheaderTypographyProps={subheaderTypographyProps} {...headerProps} /> )} @@ -250,6 +256,7 @@ export function InfoCard(props: Props): JSX.Element { <CardContent className={classNames(cardClassName, { [classes.noPadding]: noPadding, + [classes.contentAlignBottom]: alignContent === 'bottom', })} style={calculatedCardStyle} > diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx index 0503f58f1c..19834268f9 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx @@ -131,6 +131,7 @@ export type SidebarSubmenuItemProps = { icon?: IconComponent; dropdownItems?: SidebarSubmenuItemDropdownItem[]; exact?: boolean; + initialShowDropdown?: boolean; }; /** @@ -149,7 +150,9 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => { const currentLocation = useLocation(); let isActive = isLocationMatch(currentLocation, toLocation, exact); - const [showDropDown, setShowDropDown] = useState(false); + const [showDropDown, setShowDropDown] = useState( + props.initialShowDropdown ?? false, + ); const handleClickDropdown = () => { setShowDropDown(!showDropDown); }; diff --git a/packages/core-components/src/layout/SignInPage/guestProvider.tsx b/packages/core-components/src/layout/SignInPage/guestProvider.tsx index 3867caed5a..145b653a82 100644 --- a/packages/core-components/src/layout/SignInPage/guestProvider.tsx +++ b/packages/core-components/src/layout/SignInPage/guestProvider.tsx @@ -60,7 +60,11 @@ const Component: ProviderComponent = ({ discoveryApi, }); - const identityResponse = await getIdentity(identity); + const identityResponse = await getIdentity(identity).catch(error => { + // eslint-disable-next-line no-console + console.warn(`Failed to sign in as a guest, ${error}`); + return undefined; + }); if (!identityResponse) { // eslint-disable-next-line no-alert @@ -108,7 +112,11 @@ const loader: ProviderLoader = async apis => { provider: 'guest', discoveryApi: apis.get(discoveryApiRef)!, }); - const identityResponse = await getIdentity(identity); + const identityResponse = await getIdentity(identity).catch(error => { + // eslint-disable-next-line no-console + console.warn(`Failed to sign in as a guest, ${error}`); + return undefined; + }); if (!identityResponse && !useLegacyGuestToken) { return undefined; diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 1613f381dd..c6338c7243 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/create-app +## 0.5.15-next.1 + +### Patch Changes + +- Bumped create-app version. + +## 0.5.15-next.0 + +### Patch Changes + +- c066c88: Removed `packages/backend/src/types.ts` from the template as it is unused. It was mistakenly left in after moving the template to the new backend system. +- 5d99272: Update local development dependencies. +- 8105aad: Removed the Tech Radar and GitHub Actions plugins from the template, which have been moved to the community plugins repository. +- Updated dependencies + - @backstage/cli-common@0.1.13 + ## 0.5.14 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index e5ec68b219..72d8e00045 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "A CLI that helps you create your own Backstage app", - "version": "0.5.14", + "version": "0.5.15-next.1", "publishConfig": { "access": "public" }, diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index fdeaa1a203..e4622aab60 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/dev-utils +## 1.0.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/app-defaults@1.5.5-next.1 + - @backstage/integration-react@1.1.26 + +## 1.0.32-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/app-defaults@1.5.5-next.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/integration-react@1.1.26 + ## 1.0.31 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 7946625ffa..bac82890d0 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/dev-utils", - "version": "1.0.31", + "version": "1.0.32-next.1", "description": "Utilities for developing Backstage plugins.", "backstage": { "role": "web-library" diff --git a/packages/e2e-test/CHANGELOG.md b/packages/e2e-test/CHANGELOG.md index fc0bb08cdf..326002b350 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,21 @@ # e2e-test +## 0.2.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.15-next.1 + +## 0.2.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.15-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/errors@1.2.4 + ## 0.2.15 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index bc25199cb6..e4ddf47d73 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,7 +1,7 @@ { "name": "e2e-test", "description": "E2E test for verifying Backstage packages", - "version": "0.2.15", + "version": "0.2.16-next.1", "private": true, "backstage": { "role": "cli" @@ -43,7 +43,6 @@ "@backstage/cli": "workspace:^", "@types/fs-extra": "^11.0.0", "@types/node": "^18.17.8", - "@types/puppeteer": "^7.0.0", "nodemon": "^3.0.1" }, "nodemonConfig": { diff --git a/packages/frontend-app-api/CHANGELOG.md b/packages/frontend-app-api/CHANGELOG.md index 965c3e189d..0ac06f4393 100644 --- a/packages/frontend-app-api/CHANGELOG.md +++ b/packages/frontend-app-api/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/frontend-app-api +## 0.6.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + +## 0.6.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + ## 0.6.4 ### Patch Changes diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index 07b5685a1e..65045bc6ab 100644 --- a/packages/frontend-app-api/package.json +++ b/packages/frontend-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-app-api", - "version": "0.6.4", + "version": "0.6.5-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-plugin-api/CHANGELOG.md b/packages/frontend-plugin-api/CHANGELOG.md index 6a98ec9810..1c681fa6bc 100644 --- a/packages/frontend-plugin-api/CHANGELOG.md +++ b/packages/frontend-plugin-api/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/frontend-plugin-api +## 0.6.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## 0.6.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + ## 0.6.4 ### Patch Changes diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index ca56db2970..94906cfc67 100644 --- a/packages/frontend-plugin-api/package.json +++ b/packages/frontend-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-plugin-api", - "version": "0.6.4", + "version": "0.6.5-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-test-utils/CHANGELOG.md b/packages/frontend-test-utils/CHANGELOG.md index c35b54f728..79d54b2f29 100644 --- a/packages/frontend-test-utils/CHANGELOG.md +++ b/packages/frontend-test-utils/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/frontend-test-utils +## 0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-app-api@0.6.5-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + +## 0.1.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-app-api@0.6.5-next.0 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/test-utils@1.5.5-next.0 + - @backstage/types@1.1.1 + ## 0.1.6 ### Patch Changes diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index d249878024..af83141368 100644 --- a/packages/frontend-test-utils/package.json +++ b/packages/frontend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-test-utils", - "version": "0.1.6", + "version": "0.1.7-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index 6b691dff80..2029dac600 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -513,6 +513,17 @@ export function getGitLabRequestOptions(config: GitLabIntegrationConfig): { headers: Record<string, string>; }; +// @public +export function getHarnessFileContentsUrl( + config: HarnessIntegrationConfig, + url: string, +): string; + +// @public +export function getHarnessRequestOptions(config: HarnessIntegrationConfig): { + headers?: Record<string, string>; +}; + // @public export class GiteaIntegration implements ScmIntegration { constructor(config: GiteaIntegrationConfig); @@ -674,6 +685,34 @@ export type GoogleGcsIntegrationConfig = { privateKey?: string; }; +// @public +export class HarnessIntegration implements ScmIntegration { + constructor(config: HarnessIntegrationConfig); + // (undocumented) + readonly config: HarnessIntegrationConfig; + // (undocumented) + static factory: ScmIntegrationsFactory<HarnessIntegration>; + // (undocumented) + resolveEditUrl(url: string): string; + // (undocumented) + resolveUrl(options: { + url: string; + base: string; + lineNumber?: number | undefined; + }): string; + // (undocumented) + get title(): string; + // (undocumented) + get type(): string; +} + +// @public +export type HarnessIntegrationConfig = { + host: string; + token?: string; + apiKey?: string; +}; + // @public export interface IntegrationsByType { // (undocumented) @@ -696,6 +735,8 @@ export interface IntegrationsByType { github: ScmIntegrationsGroup<GithubIntegration>; // (undocumented) gitlab: ScmIntegrationsGroup<GitLabIntegration>; + // (undocumented) + harness: ScmIntegrationsGroup<HarnessIntegration>; } // @public @@ -839,6 +880,9 @@ export function readGoogleGcsIntegrationConfig( config: Config, ): GoogleGcsIntegrationConfig; +// @public +export function readHarnessConfig(config: Config): HarnessIntegrationConfig; + // @public @deprecated (undocumented) export const replaceGitHubUrlType: typeof replaceGithubUrlType; @@ -889,6 +933,8 @@ export interface ScmIntegrationRegistry github: ScmIntegrationsGroup<GithubIntegration>; // (undocumented) gitlab: ScmIntegrationsGroup<GitLabIntegration>; + // (undocumented) + harness: ScmIntegrationsGroup<HarnessIntegration>; resolveEditUrl(url: string): string; resolveUrl(options: { url: string; @@ -927,6 +973,8 @@ export class ScmIntegrations implements ScmIntegrationRegistry { // (undocumented) get gitlab(): ScmIntegrationsGroup<GitLabIntegration>; // (undocumented) + get harness(): ScmIntegrationsGroup<HarnessIntegration>; + // (undocumented) list(): ScmIntegration[]; // (undocumented) resolveEditUrl(url: string): string; diff --git a/packages/integration/config.d.ts b/packages/integration/config.d.ts index 44303bf25c..58dd9153c4 100644 --- a/packages/integration/config.d.ts +++ b/packages/integration/config.d.ts @@ -345,5 +345,23 @@ export interface Config { */ password?: string; }>; + /** Integration configuration for Harness Code */ + harness?: Array<{ + /** + * The hostname of the given Harness Code instance + * @visibility frontend + */ + host: string; + /** + * The apikey to use for authenticated requests. + * @visibility secret + */ + apiKey?: string; + /** + * Harness Code token used to authenticate requests. This can be either a generated access token. + * @visibility secret + */ + token?: string; + }>; }; } diff --git a/packages/integration/src/ScmIntegrations.test.ts b/packages/integration/src/ScmIntegrations.test.ts index acf602e38d..c758816c03 100644 --- a/packages/integration/src/ScmIntegrations.test.ts +++ b/packages/integration/src/ScmIntegrations.test.ts @@ -38,6 +38,7 @@ import { ScmIntegrations } from './ScmIntegrations'; import { GiteaIntegration, GiteaIntegrationConfig } from './gitea'; import { AwsCodeCommitIntegration } from './awsCodeCommit/AwsCodeCommitIntegration'; import { AwsCodeCommitIntegrationConfig } from './awsCodeCommit'; +import { HarnessIntegration, HarnessIntegrationConfig } from './harness'; describe('ScmIntegrations', () => { const awsS3 = new AwsS3Integration({ @@ -80,6 +81,10 @@ describe('ScmIntegrations', () => { host: 'gitea.local', } as GiteaIntegrationConfig); + const harness = new HarnessIntegration({ + host: 'harness.local', + } as HarnessIntegrationConfig); + const i = new ScmIntegrations({ awsS3: basicIntegrations([awsS3], item => item.config.host), awsCodeCommit: basicIntegrations([awsCodeCommit], item => item.config.host), @@ -94,6 +99,7 @@ describe('ScmIntegrations', () => { github: basicIntegrations([github], item => item.config.host), gitlab: basicIntegrations([gitlab], item => item.config.host), gitea: basicIntegrations([gitea], item => item.config.host), + harness: basicIntegrations([harness], item => item.config.host), }); it('can get the specifics', () => { @@ -113,6 +119,7 @@ describe('ScmIntegrations', () => { expect(i.github.byUrl('https://github.local')).toBe(github); expect(i.gitlab.byUrl('https://gitlab.local')).toBe(gitlab); expect(i.gitea.byUrl('https://gitea.local')).toBe(gitea); + expect(i.harness.byUrl('https://harness.local')).toBe(harness); }); it('can list', () => { @@ -128,6 +135,7 @@ describe('ScmIntegrations', () => { github, gitlab, gitea, + harness, ]), ); }); @@ -143,6 +151,7 @@ describe('ScmIntegrations', () => { expect(i.byUrl('https://github.local')).toBe(github); expect(i.byUrl('https://gitlab.local')).toBe(gitlab); expect(i.byUrl('https://gitea.local')).toBe(gitea); + expect(i.byUrl('https://harness.local')).toBe(harness); expect(i.byHost('awss3.local')).toBe(awsS3); expect(i.byHost('awscodecommit.local')).toBe(awsCodeCommit); diff --git a/packages/integration/src/ScmIntegrations.ts b/packages/integration/src/ScmIntegrations.ts index 6a1faaa75b..e0d430d834 100644 --- a/packages/integration/src/ScmIntegrations.ts +++ b/packages/integration/src/ScmIntegrations.ts @@ -28,6 +28,7 @@ import { defaultScmResolveUrl } from './helpers'; import { ScmIntegration, ScmIntegrationsGroup } from './types'; import { ScmIntegrationRegistry } from './registry'; import { GiteaIntegration } from './gitea'; +import { HarnessIntegration } from './harness/HarnessIntegration'; /** * The set of supported integrations. @@ -48,6 +49,7 @@ export interface IntegrationsByType { github: ScmIntegrationsGroup<GithubIntegration>; gitlab: ScmIntegrationsGroup<GitLabIntegration>; gitea: ScmIntegrationsGroup<GiteaIntegration>; + harness: ScmIntegrationsGroup<HarnessIntegration>; } /** @@ -70,6 +72,7 @@ export class ScmIntegrations implements ScmIntegrationRegistry { github: GithubIntegration.factory({ config }), gitlab: GitLabIntegration.factory({ config }), gitea: GiteaIntegration.factory({ config }), + harness: HarnessIntegration.factory({ config }), }); } @@ -120,6 +123,10 @@ export class ScmIntegrations implements ScmIntegrationRegistry { return this.byType.gitea; } + get harness(): ScmIntegrationsGroup<HarnessIntegration> { + return this.byType.harness; + } + list(): ScmIntegration[] { return Object.values(this.byType).flatMap( i => i.list() as ScmIntegration[], diff --git a/packages/integration/src/harness/HarnessIntegration.test.ts b/packages/integration/src/harness/HarnessIntegration.test.ts new file mode 100644 index 0000000000..2d85204f16 --- /dev/null +++ b/packages/integration/src/harness/HarnessIntegration.test.ts @@ -0,0 +1,123 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ConfigReader } from '@backstage/config'; +import { HarnessIntegration } from './HarnessIntegration'; + +describe('HarnessIntegration', () => { + it('has a working factory', () => { + const integrations = HarnessIntegration.factory({ + config: new ConfigReader({ + integrations: { + harness: [ + { + host: 'app.harness.io', + token: '1234', + }, + ], + }, + }), + }); + expect(integrations.list().length).toBe(1); + expect(integrations.list()[0].config.host).toBe('app.harness.io'); + }); + + it('returns the basics', () => { + const integration = new HarnessIntegration({ + host: 'app.harness.io', + }); + expect(integration.type).toBe('harness'); + expect(integration.title).toBe('app.harness.io'); + }); + + describe('resolveUrl', () => { + it('works for valid urls, ignoring line number', () => { + const integration = new HarnessIntegration({ + host: 'app.harness.io', + }); + + expect( + integration.resolveUrl({ + url: 'https://app.harness.io/catalog-info.yaml', + base: 'https://app.harness.io/catalog-info.yaml', + lineNumber: 9, + }), + ).toBe('https://app.harness.io/catalog-info.yaml'); + }); + + it('handles line numbers', () => { + const integration = new HarnessIntegration({ + host: 'app.harness.io', + }); + + expect( + integration.resolveUrl({ + url: '', + base: 'https://app.harness.io/catalog-info.yaml#4', + lineNumber: 9, + }), + ).toBe('https://app.harness.io/catalog-info.yaml#L9'); + }); + }); + + describe('resolves with a relative url', () => { + it('works for valid urls', () => { + const integration = new HarnessIntegration({ + host: 'app.harness.io', + }); + + expect( + integration.resolveUrl({ + url: './skeleton', + base: 'https://app.harness.io/git/plugins/repo/+/refs/heads/master/template.yaml', + }), + ).toBe( + 'https://app.harness.io/git/plugins/repo/+/refs/heads/master/skeleton', + ); + }); + }); + + describe('resolves with an absolute url', () => { + it('works for valid urls', () => { + const integration = new HarnessIntegration({ + host: 'app.harness.io', + }); + + expect( + integration.resolveUrl({ + url: '/catalog-info.yaml', + base: 'https://app.harness.io/git/repo/+/refs/heads/master/', + }), + ).toBe( + 'https://app.harness.io/git/repo/+/refs/heads/master/catalog-info.yaml', + ); + }); + }); + + it('resolve edit URL', () => { + const integration = new HarnessIntegration({ + host: 'app.harness.io', + }); + + expect( + integration.resolveEditUrl( + 'https://app.harness.io/ng/account/accountId/module/code/orgs/orgName/projects/projName/repos/repoName/edit/refMain/~/all-apis.yaml', + ), + ).toBe( + 'https://app.harness.io/gateway/code/api/v1/repos/accountId/orgName/projName/repoName/+/edit/all-apis.yaml', + ); + }); +}); diff --git a/packages/integration/src/harness/HarnessIntegration.ts b/packages/integration/src/harness/HarnessIntegration.ts new file mode 100644 index 0000000000..5d1b274149 --- /dev/null +++ b/packages/integration/src/harness/HarnessIntegration.ts @@ -0,0 +1,58 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { basicIntegrations, defaultScmResolveUrl } from '../helpers'; +import { ScmIntegration, ScmIntegrationsFactory } from '../types'; +import { HarnessIntegrationConfig, readHarnessConfig } from './config'; +import { getHarnessEditContentsUrl } from './core'; + +/** + * A Harness Code based integration. + * + * @public + */ +export class HarnessIntegration implements ScmIntegration { + static factory: ScmIntegrationsFactory<HarnessIntegration> = ({ config }) => { + const configs = config.getOptionalConfigArray('integrations.harness') ?? []; + const harnessConfigs = configs.map(c => readHarnessConfig(c)); + + return basicIntegrations( + harnessConfigs.map(c => new HarnessIntegration(c)), + (harness: HarnessIntegration) => harness.config.host, + ); + }; + + constructor(readonly config: HarnessIntegrationConfig) {} + + get type(): string { + return 'harness'; + } + + get title(): string { + return this.config.host; + } + + resolveUrl(options: { + url: string; + base: string; + lineNumber?: number | undefined; + }): string { + return defaultScmResolveUrl(options); + } + + resolveEditUrl(url: string): string { + return getHarnessEditContentsUrl(this.config, url); + } +} diff --git a/packages/integration/src/harness/config.test.ts b/packages/integration/src/harness/config.test.ts new file mode 100644 index 0000000000..93be1fcc32 --- /dev/null +++ b/packages/integration/src/harness/config.test.ts @@ -0,0 +1,98 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config, ConfigReader } from '@backstage/config'; +import { loadConfigSchema } from '@backstage/config-loader'; +import { HarnessIntegrationConfig, readHarnessConfig } from './config'; + +describe('readHarnessConfig', () => { + function buildConfig(data: Partial<HarnessIntegrationConfig>): Config { + return new ConfigReader(data); + } + + async function buildFrontendConfig( + data: Partial<HarnessIntegrationConfig>, + ): Promise<Config> { + const fullSchema = await loadConfigSchema({ + dependencies: ['@backstage/integration'], + }); + const serializedSchema = fullSchema.serialize() as { + schemas: { value: { properties?: { integrations?: object } } }[]; + }; + const schema = await loadConfigSchema({ + serialized: { + ...serializedSchema, // only include schemas that apply to integrations + schemas: serializedSchema.schemas.filter( + s => s.value?.properties?.integrations, + ), + }, + }); + const processed = schema.process( + [{ data: { integrations: { harness: [data] } }, context: 'app' }], + { visibility: ['frontend'] }, + ); + return new ConfigReader((processed[0].data as any).integrations.harness[0]); + } + + it('reads all values', () => { + const output = readHarnessConfig( + buildConfig({ + host: 'a.com', + token: 'p', + apiKey: 'a', + }), + ); + expect(output).toEqual({ + host: 'a.com', + token: 'p', + apiKey: 'a', + }); + }); + + it('can create a default value if the API base URL is missing', () => { + const output = readHarnessConfig( + buildConfig({ + host: 'a.com', + }), + ); + expect(output).toEqual({ + host: 'a.com', + token: undefined, + }); + }); + + it('rejects funky configs', () => { + const valid: any = { + host: 'a.com', + }; + expect(() => readHarnessConfig(buildConfig({ ...valid, host: 2 }))).toThrow( + /host/, + ); + }); + + it('works on the frontend', async () => { + expect( + readHarnessConfig( + await buildFrontendConfig({ + host: 'a.com', + token: 'p', + }), + ), + ).toEqual({ + host: 'a.com', + }); + }); +}); diff --git a/packages/integration/src/harness/config.ts b/packages/integration/src/harness/config.ts new file mode 100644 index 0000000000..2f75915567 --- /dev/null +++ b/packages/integration/src/harness/config.ts @@ -0,0 +1,61 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config } from '@backstage/config'; +import { isValidHost } from '../helpers'; + +/** + * The configuration for a single Harness integration. + * + * @public + */ +export type HarnessIntegrationConfig = { + /** + * The host of the target that this matches on, e.g. "app.harness.io" + */ + host: string; + /** + * The password or http token to use for authentication. + */ + token?: string; + /** + * The API key to use for authentication. + */ + apiKey?: string; +}; + +/** + * Parses a location config block for use in HarnessIntegration + * + * @public + */ +export function readHarnessConfig(config: Config): HarnessIntegrationConfig { + const host = config.getString('host'); + const token = config.getOptionalString('token'); + const apiKey = config.getOptionalString('apiKey'); + + if (!isValidHost(host)) { + throw new Error( + `Invalid Harness Code integration config, '${host}' is not a valid host`, + ); + } + + return { + host, + apiKey, + token, + }; +} diff --git a/packages/integration/src/harness/core.test.ts b/packages/integration/src/harness/core.test.ts new file mode 100644 index 0000000000..d3d402806a --- /dev/null +++ b/packages/integration/src/harness/core.test.ts @@ -0,0 +1,91 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { setupServer } from 'msw/node'; +import { setupRequestMockHandlers } from '../helpers'; +import { HarnessIntegrationConfig } from './config'; +import { + getHarnessEditContentsUrl, + getHarnessFileContentsUrl, + getHarnessRequestOptions, +} from './core'; + +describe('Harness code core', () => { + const worker = setupServer(); + setupRequestMockHandlers(worker); + + describe('getHarnessFileContentsUrl', () => { + it('can create an url from arguments', () => { + const config: HarnessIntegrationConfig = { + host: 'app.harness.io', + }; + expect( + getHarnessFileContentsUrl( + config, + 'https://app.harness.io/ng/account/accountId/module/code/orgs/orgName/projects/projName/repos/repoName/files/refMain/~/all-apis.yaml', + ), + ).toEqual( + 'https://app.harness.io/gateway/code/api/v1/repos/accountId/orgName/projName/repoName/+/raw/all-apis.yaml?routingId=accountId&git_ref=refMain', + ); + }); + }); + + describe('getHarnessEditContentsUrl', () => { + it('can create an url from arguments', () => { + const config: HarnessIntegrationConfig = { + host: 'app.harness.io', + }; + expect( + getHarnessEditContentsUrl( + config, + 'https://app.harness.io/ng/account/accountId/module/code/orgs/orgName/projects/projName/repos/repoName/edit/refMain/~/all-apis.yaml', + ), + ).toEqual( + 'https://app.harness.io/gateway/code/api/v1/repos/accountId/orgName/projName/repoName/+/edit/all-apis.yaml', + ); + }); + }); + + describe('getHarnessRequestOptions', () => { + it('adds token header when only a token is specified', () => { + const authRequest: HarnessIntegrationConfig = { + host: 'app.harness.io', + token: 'P', + }; + const anonymousRequest: HarnessIntegrationConfig = { + host: 'app.harness.io', + }; + expect( + (getHarnessRequestOptions(authRequest).headers as any).Authorization, + ).toEqual('Bearer P'); + expect(getHarnessRequestOptions(anonymousRequest).headers).toStrictEqual( + {}, + ); + }); + + it('adds basic auth when apikey and token are specified', () => { + const authRequest: HarnessIntegrationConfig = { + host: 'app.harness.io', + token: 'P', + apiKey: 'a', + }; + + expect( + (getHarnessRequestOptions(authRequest).headers as any)['x-api-key'], + ).toEqual('a'); + }); + }); +}); diff --git a/packages/integration/src/harness/core.ts b/packages/integration/src/harness/core.ts new file mode 100644 index 0000000000..3a61905e27 --- /dev/null +++ b/packages/integration/src/harness/core.ts @@ -0,0 +1,132 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { HarnessIntegrationConfig } from './config'; + +/** + * Given a URL pointing to a file, returns a URL + * for editing the contents of the data. + * + * @remarks + * + * Converts + * from: https://app.harness.io/a/b/src/branchname/path/to/c.yaml + * or: https://app.harness.io/a/b/_edit/branchname/path/to/c.yaml + * + * @param url - A URL pointing to a file + * @param config - The relevant provider config + * @public + */ +export function getHarnessEditContentsUrl( + config: HarnessIntegrationConfig, + url: string, +) { + try { + const baseUrl = `https://${config.host}`; + const [ + _blank, + _ng, + _account, + accountId, + _module, + _moduleName, + _org, + orgName, + _projects, + projectName, + _repos, + repoName, + _files, + _ref, + _branch, + ...path + ] = url.replace(baseUrl, '').split('/'); + const pathWithoutSlash = path.join('/').replace(/^\//, ''); + return `${baseUrl}/gateway/code/api/v1/repos/${accountId}/${orgName}/${projectName}/${repoName}/+/edit/${pathWithoutSlash}`; + } catch (e) { + throw new Error(`Incorrect URL: ${url}, ${e}`); + } +} + +/** + * Given a file path URL, + * it returns an API URL which returns the contents of the file. + * @remarks + * + * Converts + * from: https://app.harness.io/ng/account/accountId/module/code/orgs/orgName/projects/projName/repos/repoName/files/refMain/~/all-apis.yaml + * to: https://app.harness.io/gateway/code/api/v1/repos/accountId/orgName/projName/repoName/+/content/all-apis.yaml?routingId=accountId&include_commit=false&ref=refMain + * + * @param url - A URL pointing to a file + * @param config - The relevant provider config + * @public + */ +export function getHarnessFileContentsUrl( + config: HarnessIntegrationConfig, + url: string, +) { + try { + const baseUrl = `https://${config.host}`; + const [ + _blank, + _ng, + _account, + accountId, + _module, + _moduleName, + _org, + orgName, + _projects, + projectName, + _repos, + repoName, + _files, + _ref, + _branch, + ...path + ] = url.replace(baseUrl, '').split('/'); + const urlParts = url.replace(baseUrl, '').split('/'); + const refAndPath = urlParts.slice(13); + const refIndex = refAndPath.findIndex(item => item === '~'); + const refString = refAndPath.slice(0, refIndex); + const pathWithoutSlash = path.join('/').replace(/^\//, ''); + return `${baseUrl}/gateway/code/api/v1/repos/${accountId}/${orgName}/${projectName}/${repoName}/+/raw/${pathWithoutSlash}?routingId=${accountId}&git_ref=${refString}`; + } catch (e) { + throw new Error(`Incorrect URL: ${url}, ${e}`); + } +} + +/** + * Return request headers for a Harness Code provider. + * + * @param config - A Harness Code provider config + * @public + */ +export function getHarnessRequestOptions(config: HarnessIntegrationConfig): { + headers?: Record<string, string>; +} { + const headers: Record<string, string> = {}; + const { token, apiKey } = config; + + if (apiKey) { + headers['x-api-key'] = apiKey; + } else if (token) { + headers.Authorization = `Bearer ${token}`; + } + + return { + headers, + }; +} diff --git a/packages/integration/src/harness/index.ts b/packages/integration/src/harness/index.ts new file mode 100644 index 0000000000..264df657f0 --- /dev/null +++ b/packages/integration/src/harness/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { HarnessIntegration } from './HarnessIntegration'; +export { getHarnessRequestOptions, getHarnessFileContentsUrl } from './core'; +export { readHarnessConfig } from './config'; +export type { HarnessIntegrationConfig } from './config'; diff --git a/packages/integration/src/index.ts b/packages/integration/src/index.ts index b20477ee6a..32c573abef 100644 --- a/packages/integration/src/index.ts +++ b/packages/integration/src/index.ts @@ -31,6 +31,7 @@ export * from './gitea'; export * from './github'; export * from './gitlab'; export * from './googleGcs'; +export * from './harness'; export { defaultScmResolveUrl } from './helpers'; export { ScmIntegrations } from './ScmIntegrations'; export type { IntegrationsByType } from './ScmIntegrations'; diff --git a/packages/integration/src/registry.ts b/packages/integration/src/registry.ts index 00706acc1c..7e4b34cc34 100644 --- a/packages/integration/src/registry.ts +++ b/packages/integration/src/registry.ts @@ -25,6 +25,7 @@ import { GerritIntegration } from './gerrit/GerritIntegration'; import { GithubIntegration } from './github/GithubIntegration'; import { GitLabIntegration } from './gitlab/GitLabIntegration'; import { GiteaIntegration } from './gitea/GiteaIntegration'; +import { HarnessIntegration } from './harness/HarnessIntegration'; /** * Holds all registered SCM integrations, of all types. @@ -46,6 +47,7 @@ export interface ScmIntegrationRegistry github: ScmIntegrationsGroup<GithubIntegration>; gitlab: ScmIntegrationsGroup<GitLabIntegration>; gitea: ScmIntegrationsGroup<GiteaIntegration>; + harness: ScmIntegrationsGroup<HarnessIntegration>; /** * Resolves an absolute or relative URL in relation to a base URL. * diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index a3af98d211..443a5f30b5 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/repo-tools +## 0.8.1-next.1 + +### Patch Changes + +- 9ae9bb2: Update the paths logic in the api reports command to support complex subpaths +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/cli-node@0.2.5 + - @backstage/config-loader@1.8.0 + +## 0.8.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.5 + - @backstage/config-loader@1.8.0 + - @backstage/errors@1.2.4 + ## 0.8.0 ### Minor Changes diff --git a/packages/repo-tools/cli-report.md b/packages/repo-tools/cli-report.md index 9d37ce7aff..a06f426baf 100644 --- a/packages/repo-tools/cli-report.md +++ b/packages/repo-tools/cli-report.md @@ -172,9 +172,20 @@ Commands: lint [options] [paths...] test [options] [paths...] fuzz [options] + diff [options] help [command] ``` +### `backstage-repo-tools repo schema openapi diff` + +``` +Usage: backstage-repo-tools repo schema openapi diff [options] + +Options: + --since <ref> + -h, --help +``` + ### `backstage-repo-tools repo schema openapi fuzz` ``` diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index e3ba518ee2..e39e5203b7 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/repo-tools", "description": "CLI for Backstage repo tooling ", - "version": "0.8.0", + "version": "0.8.1-next.1", "publishConfig": { "access": "public" }, @@ -49,6 +49,7 @@ "@stoplight/spectral-rulesets": "^1.18.0", "@stoplight/spectral-runtime": "^1.1.2", "@stoplight/types": "^14.0.0", + "@useoptic/openapi-utilities": "^0.54.8", "chalk": "^4.0.0", "codeowners-utils": "^1.0.2", "command-exists": "^1.2.9", diff --git a/packages/repo-tools/src/commands/api-reports/api-extractor.ts b/packages/repo-tools/src/commands/api-reports/api-extractor.ts index 7e36b08b0a..6a523af6a9 100644 --- a/packages/repo-tools/src/commands/api-reports/api-extractor.ts +++ b/packages/repo-tools/src/commands/api-reports/api-extractor.ts @@ -62,7 +62,7 @@ import { IMarkdownEmitterContext } from '@microsoft/api-documenter/lib/markdown/ import { AstDeclaration } from '@microsoft/api-extractor/lib/analyzer/AstDeclaration'; import { paths as cliPaths } from '../../lib/paths'; import { minimatch } from 'minimatch'; -import { getPackageExportNames } from '../../lib/entryPoints'; +import { getPackageExportDetails } from '../../lib/getPackageExportDetails'; import { createBinRunner } from '../util'; const tmpDir = cliPaths.resolveTargetRoot( @@ -303,8 +303,15 @@ function logApiReportInstructions() { async function findPackageEntryPoints(packageDirs: string[]): Promise< Array<{ + // package dir relative to root, e.g. "packages/backend-app-api" packageDir: string; + // the name of the export, e.g. "index" or "alpha" name: string; + // the path within the dist directory for this export, e.g. "alpha.d.ts" + distPath: string; + // the path within the dist-types directory of this package for this export, + // e.g. "src/entrypoints/foo/index.d.ts" + distTypesPath: string; }> > { return Promise.all( @@ -313,12 +320,9 @@ async function findPackageEntryPoints(packageDirs: string[]): Promise< cliPaths.resolveTargetRoot(packageDir, 'package.json'), ); - return ( - getPackageExportNames(pkg)?.map(name => ({ packageDir, name })) ?? { - packageDir, - name: 'index', - } - ); + return getPackageExportDetails(pkg).map(details => { + return { packageDir, ...details }; + }); }), ).then(results => results.flat()); } @@ -344,13 +348,23 @@ export async function runApiExtraction({ }: ApiExtractionOptions) { await fs.remove(outputDir); - const packageEntryPoints = await findPackageEntryPoints(packageDirs); + // The collection of all entry points of all packages, as a single list + const allEntryPoints = await findPackageEntryPoints(packageDirs); - const entryPoints = packageEntryPoints.map(({ packageDir, name }) => { - return cliPaths.resolveTargetRoot( - `./dist-types/${packageDir}/src/${name}.d.ts`, - ); - }); + // The path (relative to the root) to ALL dist-types entry points (e.g. + // "dist-types/packages/backend-app-api/src/index.d.ts"). These are used as + // "extra"/contextual entry points for the extractor so that it can see the + // full context of things that are required by the local entry point being + // inspected. + const allDistTypesEntryPointPaths = allEntryPoints.map( + ({ packageDir, distTypesPath }) => { + return cliPaths.resolveTargetRoot( + './dist-types', + packageDir, + distTypesPath, + ); + }, + ); let compilerState: CompilerState | undefined = undefined; @@ -364,8 +378,8 @@ export async function runApiExtraction({ } const warnings = new Array<string>(); - for (const [packageDir, group] of Object.entries( - groupBy(packageEntryPoints, ep => ep.packageDir), + for (const [packageDir, packageEntryPoints] of Object.entries( + groupBy(allEntryPoints, ep => ep.packageDir), )) { console.log(`## Processing ${packageDir}`); const noBail = Array.isArray(allowWarnings) @@ -377,7 +391,6 @@ export async function runApiExtraction({ './dist-types', packageDir, ); - const names = group.map(ep => ep.name); const remainingReportFiles = new Set( fs @@ -389,8 +402,9 @@ export async function runApiExtraction({ ), ); - for (const name of names) { - const suffix = name === 'index' ? '' : `-${name}`; + for (const packageEntryPoint of packageEntryPoints) { + const suffix = + packageEntryPoint.name === 'index' ? '' : `-${packageEntryPoint.name}`; const reportFileName = `api-report${suffix}.md`; const reportPath = resolvePath(projectFolder, reportFileName); remainingReportFiles.delete(reportFileName); @@ -401,7 +415,7 @@ export async function runApiExtraction({ configObject: { mainEntryPointFilePath: resolvePath( packageFolder, - `src/${name}.d.ts`, + packageEntryPoint.distTypesPath, ), bundledPackages: [], @@ -422,7 +436,7 @@ export async function runApiExtraction({ docModel: { // TODO(Rugvip): This skips docs for non-index entry points. We can try to work around it, but // most likely it makes sense to wait for API Extractor to natively support exports. - enabled: name === 'index', + enabled: packageEntryPoint.name === 'index', apiJsonFilePath: resolvePath( outputDir, `<unscopedPackageName>${suffix}.api.json`, @@ -482,7 +496,7 @@ export async function runApiExtraction({ if (!compilerState) { compilerState = CompilerState.create(extractorConfig, { - additionalEntryPoints: entryPoints, + additionalEntryPoints: allDistTypesEntryPointPaths, }); } @@ -519,18 +533,21 @@ export async function runApiExtraction({ }); // This release tag validation makes sure that the release tag of known entry points match expectations. - // The root index entrypoint is only allowed @public exports, while /alpha and /beta only allow @alpha and @beta. + // The root index entry point is only allowed @public exports, while /alpha and /beta only allow @alpha and @beta. if ( validateReleaseTags && fs.pathExistsSync(extractorConfig.reportFilePath) ) { - if (['index', 'alpha', 'beta'].includes(name)) { + if (['index', 'alpha', 'beta'].includes(packageEntryPoint.name)) { const report = await fs.readFile( extractorConfig.reportFilePath, 'utf8', ); const lines = report.split(/\r?\n/); - const expectedTag = name === 'index' ? 'public' : name; + const expectedTag = + packageEntryPoint.name === 'index' + ? 'public' + : packageEntryPoint.name; for (let i = 0; i < lines.length; i += 1) { const line = lines[i]; const match = line.match(/^\/\/ @(alpha|beta|public)/); diff --git a/packages/repo-tools/src/commands/api-reports/generateTypeDeclarations.ts b/packages/repo-tools/src/commands/api-reports/generateTypeDeclarations.ts index 58f4117bc2..43ee2aa85f 100644 --- a/packages/repo-tools/src/commands/api-reports/generateTypeDeclarations.ts +++ b/packages/repo-tools/src/commands/api-reports/generateTypeDeclarations.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import fs from 'fs-extra'; import { spawnSync } from 'child_process'; import { paths as cliPaths } from '../../lib/paths'; @@ -27,7 +28,6 @@ import { paths as cliPaths } from '../../lib/paths'; * @param tsconfigFilePath {string} The path to the `tsconfig.json` file to use for generating the declaration files. * @returns {Promise<void>} A promise that resolves when the declaration files have been generated. */ - export async function generateTypeDeclarations(tsconfigFilePath: string) { await fs.remove(cliPaths.resolveTargetRoot('dist-types')); const { status } = spawnSync( diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index d2fe507941..83266ea9f9 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -78,6 +78,15 @@ function registerPackageCommand(program: Command) { .action( lazy(() => import('./package/schema/openapi/fuzz').then(m => m.command)), ); + + openApiCommand + .command('diff') + .option('--ignore', 'Ignore linting failures and only log the results.') + .option('--json', 'Output the results as JSON') + .option('--since <ref>', 'Diff the API against a specific ref') + .action( + lazy(() => import('./package/schema/openapi/diff').then(m => m.command)), + ); } function registerRepoCommand(program: Command) { @@ -96,7 +105,7 @@ function registerRepoCommand(program: Command) { openApiCommand .command('verify [paths...]') .description( - 'Verify that all OpenAPI schemas are valid and have a matching `schemas/openapi.generated.ts` file.', + 'Verify that all OpenAPI schemas are valid and set up correctly.', ) .action( lazy(() => @@ -133,6 +142,20 @@ function registerRepoCommand(program: Command) { .action( lazy(() => import('./repo/schema/openapi/fuzz').then(m => m.command)), ); + + openApiCommand + .command('diff') + .description( + 'Diff the repository against a specific ref, will run all package `diff` scripts.', + ) + .option( + '--since <ref>', + 'Diff the API against a specific ref', + 'origin/master', + ) + .action( + lazy(() => import('./repo/schema/openapi/diff').then(m => m.command)), + ); } export function registerCommands(program: Command) { diff --git a/packages/repo-tools/src/commands/package/schema/openapi/diff.ts b/packages/repo-tools/src/commands/package/schema/openapi/diff.ts new file mode 100644 index 0000000000..3cb0baf313 --- /dev/null +++ b/packages/repo-tools/src/commands/package/schema/openapi/diff.ts @@ -0,0 +1,93 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import chalk from 'chalk'; +import { exec } from '../../../../lib/exec'; +import { getPathToCurrentOpenApiSpec } from '../../../../lib/openapi/helpers'; +import { paths as cliPaths } from '../../../../lib/paths'; +import { OptionValues } from 'commander'; +import { env } from 'process'; +import { readFile, rm } from 'fs/promises'; +import { resolve } from 'path'; + +const reduceOpticOutput = (output: string) => { + return output + .split('\n') + .filter(e => !e.startsWith('Rerun') && e.trim()) + .join('\n'); +}; + +async function check(opts: OptionValues) { + const resolvedOpenapiPath = await getPathToCurrentOpenApiSpec(); + + let baseRef = opts.since; + if (!baseRef) { + const { stdout: branch } = await exec( + 'git merge-base --fork-point origin/master', + ); + baseRef = branch.toString().trim(); + } + + let failed = false; + let output = ''; + try { + const { stdout } = await exec( + 'yarn optic diff', + [ + resolvedOpenapiPath, + '--check', + opts.json ? '--json' : '', + '--base', + baseRef, + ], + { + cwd: cliPaths.targetRoot, + env: { CI: opts.json ? '1' : undefined, ...env }, + }, + ); + output = stdout.toString(); + } catch (err) { + output = err.stdout; + failed = true; + } + + if (opts.json) { + const file = ( + await readFile(resolve(cliPaths.targetRoot, 'ci-run-details.json')) + ).toString(); + const results = JSON.parse(file); + console.log(file); + if (!opts.ignore && results.failed) { + throw new Error('Some checks failed'); + } + + await rm(resolve(cliPaths.targetRoot, 'ci-run-details.json')); + } else { + console.log(reduceOpticOutput(output)); + if (!opts.ignore && failed) { + throw new Error('Some checks failed'); + } + } +} + +export async function command(opts: OptionValues) { + try { + await check(opts); + if (!opts.json) console.log(chalk.green(`All checks passed.`)); + } catch (err) { + if (!opts.json) console.log(chalk.red(err.message)); + process.exit(1); + } +} diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/diff.ts b/packages/repo-tools/src/commands/repo/schema/openapi/diff.ts new file mode 100644 index 0000000000..02f3db0c43 --- /dev/null +++ b/packages/repo-tools/src/commands/repo/schema/openapi/diff.ts @@ -0,0 +1,111 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { PackageGraph } from '@backstage/cli-node'; +import { OptionValues } from 'commander'; +import { exec } from '../../../../lib/exec'; +import { + CiRunDetails, + generateCompareSummaryMarkdown, +} from '../../../../lib/openapi/optic/helpers'; +import { paths as cliPaths } from '../../../../lib/paths'; +import { YAML_SCHEMA_PATH } from '../../../../lib/openapi/constants'; + +function cleanUpApiName(e: { apiName: string }) { + e.apiName = e.apiName + .replace(cliPaths.targetDir, '') + .replace(YAML_SCHEMA_PATH, ''); +} + +export async function command(opts: OptionValues) { + let packages = await PackageGraph.listTargetPackages(); + + let since = ''; + if (opts.since) { + const { stdout: sinceRaw } = await exec('git', ['rev-parse', opts.since]); + since = sinceRaw.toString().trim(); + const { stdout: changedFilesRaw } = await exec('git', [ + 'diff', + '--name-only', + since, + ]); + const changedFiles = changedFilesRaw.toString().trim(); + + const changedOpenApiSpecs = changedFiles + .split('\n') + .filter(e => e.endsWith(YAML_SCHEMA_PATH)) + .map(e => cliPaths.resolveTarget(e)); + + // filter packages by changedFiles + packages = packages.filter(pkg => + changedOpenApiSpecs.some(e => e.startsWith(`${pkg.dir}/`)), + ); + } + + const checkablePackages = packages.filter(e => e.packageJson.scripts?.diff); + + try { + const outputs = { + completed: [], + failed: [], + noop: [], + warning: [], + severity: 0, + } as CiRunDetails; + for (const pkg of checkablePackages) { + const sinceCommands = since ? ['--since', since] : []; + const { stdout } = await exec( + 'yarn', + ['diff', '--ignore', '--json', ...sinceCommands], + { + cwd: pkg.dir, + }, + ); + const result = JSON.parse(stdout.toString()); + outputs.completed.push(...(result.completed ?? [])); + outputs.failed.push(...(result.failed ?? [])); + outputs.noop.push(...(result.noop ?? [])); + } + + for (const pkg of packages.filter(e => !e.packageJson.scripts?.diff)) { + outputs.warning?.push({ + apiName: `${pkg.dir}/`, + warning: 'No diff script found in package.json', + }); + } + + outputs.completed.forEach(cleanUpApiName); + outputs.failed.forEach(cleanUpApiName); + outputs.noop.forEach(cleanUpApiName); + outputs.warning?.forEach(cleanUpApiName); + + const { stdout: currentSha } = await exec('git', ['rev-parse', 'HEAD']); + console.log( + generateCompareSummaryMarkdown( + { sha: currentSha.toString().trim() }, + outputs, + { verbose: true }, + ), + ); + + const failed = outputs.failed.length > 0; + if (failed) { + throw new Error('Some checks failed'); + } + } catch (err) { + console.error(err); + process.exit(1); + } +} diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts b/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts index 442494610c..23e07d1230 100644 --- a/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts @@ -38,7 +38,6 @@ async function verify(directoryPath: string) { // Unable to find spec at path. return; } - const yaml = YAML.load(await fs.readFile(openapiPath, 'utf8')); await Parser.validate(cloneDeep(yaml) as any); diff --git a/packages/repo-tools/src/commands/type-deps/type-deps.ts b/packages/repo-tools/src/commands/type-deps/type-deps.ts index 17164cea39..7b75ac37ee 100644 --- a/packages/repo-tools/src/commands/type-deps/type-deps.ts +++ b/packages/repo-tools/src/commands/type-deps/type-deps.ts @@ -20,7 +20,7 @@ import { resolve as resolvePath } from 'path'; // eslint-disable-next-line @backstage/no-undeclared-imports import chalk from 'chalk'; import { getPackages, Package } from '@manypkg/get-packages'; -import { getPackageExportNames } from '../../lib/entryPoints'; +import { getPackageExportDetails } from '../../lib/getPackageExportDetails'; export default async () => { const { packages } = await getPackages(resolvePath('.')); @@ -97,11 +97,9 @@ function findAllDeps(declSrc: string) { * missing or incorrect in package.json */ function checkTypes(pkg: Package) { - const entryPointNames = getPackageExportNames(pkg.packageJson) ?? ['index']; - - const allDeps = entryPointNames.flatMap(name => { + const allDeps = getPackageExportDetails(pkg.packageJson).flatMap(exp => { const typeDecl = fs.readFileSync( - resolvePath(pkg.dir, `dist/${name}.d.ts`), + resolvePath(pkg.dir, 'dist', exp.distPath), 'utf8', ); return findAllDeps(typeDecl); diff --git a/packages/repo-tools/src/lib/entryPoints.ts b/packages/repo-tools/src/lib/entryPoints.ts deleted file mode 100644 index 4a412d410b..0000000000 --- a/packages/repo-tools/src/lib/entryPoints.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2023 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { extname } from 'path'; -import type { JsonObject } from '@backstage/types'; - -export function getPackageExportNames(pkg: JsonObject): string[] | undefined { - if (pkg.exports && typeof pkg.exports !== 'string') { - return Object.entries(pkg.exports).flatMap(([mount, path]) => { - const ext = extname(String(path)); - if (!['.ts', '.tsx', '.cts', '.mts'].includes(ext)) { - return []; // Ignore non-TS entry points - } - let name = mount; - if (name.startsWith('./')) { - name = name.slice(2); - } - if (!name || name === '.') { - return ['index']; - } - return [name]; - }); - } - - return undefined; -} diff --git a/packages/repo-tools/src/lib/getPackageExportDetails.ts b/packages/repo-tools/src/lib/getPackageExportDetails.ts new file mode 100644 index 0000000000..6889bb2e77 --- /dev/null +++ b/packages/repo-tools/src/lib/getPackageExportDetails.ts @@ -0,0 +1,68 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { extname } from 'path'; +import type { JsonObject } from '@backstage/types'; + +export function getPackageExportDetails(pkg: JsonObject): Array<{ + // the name of the export, e.g. "index" or "alpha" + name: string; + // the path within the dist directory for this export, e.g. "alpha.d.ts" + distPath: string; + // the path within the dist-types directory of this package for this export, + // e.g. "src/entrypoints/foo/index.d.ts" + distTypesPath: string; +}> { + if (pkg.exports && typeof pkg.exports !== 'string') { + return Object.entries(pkg.exports).flatMap( + ([mount, path]: [string, string]) => { + const ext = extname(path); + if (!['.ts', '.tsx', '.cts', '.mts'].includes(ext)) { + return []; // Ignore non-TS entry points + } + + let name = mount; + if (name.startsWith('./')) { + name = name.slice(2); + } + if (!name || name === '.') { + name = 'index'; + } + + const distPath = `${name}.d.ts`; + const distTypesPath = path + .replace(/^\.\//, '') // Remove leading "./" + .replace(/\.[^.]+$/, '.d.ts'); // Replace .extension with .d.ts + + return [ + { + name, + distPath, + distTypesPath, + }, + ]; + }, + ); + } + + return [ + { + name: 'index', + distPath: 'index.d.ts', + distTypesPath: 'src/index.d.ts', + }, + ]; +} diff --git a/packages/repo-tools/src/lib/openapi/optic/helpers.ts b/packages/repo-tools/src/lib/openapi/optic/helpers.ts new file mode 100644 index 0000000000..9f5deb72e0 --- /dev/null +++ b/packages/repo-tools/src/lib/openapi/optic/helpers.ts @@ -0,0 +1,302 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* eslint-disable no-nested-ternary */ + +import { + compareSpecs, + groupDiffsByEndpoint, + Severity, + getOperationsChangedLabel, + getOperationsChanged, +} from '@useoptic/openapi-utilities'; +import { GroupedDiffs } from '@useoptic/openapi-utilities/build/openapi3/group-diff'; + +/** + * The below code is copied from https://github.com/opticdev/optic/blob/main/projects/optic/src/commands/ci/comment/common.ts#L82 for use + * with a security flow for forked repositories. + */ + +type Comparison = { + groupedDiffs: ReturnType<typeof groupDiffsByEndpoint>; + results: Awaited<ReturnType<typeof compareSpecs>>['results']; +}; + +export type CiRunDetails = { + completed: { + warnings: string[]; + apiName: string; + opticWebUrl?: string | null; + comparison: Comparison; + specUrl?: string | null; + capture?: any; + }[]; + warning?: { apiName: string; warning: string }[]; + failed: { apiName: string; error: string }[]; + noop: { apiName: string }[]; + severity: Severity; +}; + +const getChecksLabel = ( + results: CiRunDetails['completed'][number]['comparison']['results'], + severity: Severity, +) => { + const totalChecks = results.length; + let failingChecks = 0; + let exemptedFailingChecks = 0; + + for (const result of results) { + if (result.passed) continue; + if (result.severity < severity) continue; + if (result.exempted) exemptedFailingChecks += 1; + else failingChecks += 1; + } + + const exemptedChunk = + exemptedFailingChecks > 0 ? `, ${exemptedFailingChecks} exempted` : ''; + + return failingChecks > 0 + ? `âš ī¸ **${failingChecks}**/**${totalChecks}** failed${exemptedChunk}` + : totalChecks > 0 + ? `✅ **${totalChecks}** passed${exemptedChunk}` + : `â„šī¸ No automated checks have run`; +}; + +function getOperationsText( + groupedDiffs: GroupedDiffs, + options: { webUrl?: string | null; verbose: boolean; labelJoiner?: string }, +) { + const ops = getOperationsChanged(groupedDiffs); + + const operationsText = options.verbose + ? [ + ...[...ops.added].map(o => `\`${o}\` (added)`), + ...[...ops.changed].map(o => `\`${o}\` (changed)`), + ...[...ops.removed].map(o => `\`${o}\` (removed)`), + ].join('\n') + : ''; + return `${getOperationsChangedLabel(groupedDiffs)} + + ${operationsText} + `; +} + +const getCaptureIssuesLabel = ({ + unmatchedInteractions, + mismatchedEndpoints, +}: { + unmatchedInteractions: number; + mismatchedEndpoints: number; +}) => { + return [ + ...(unmatchedInteractions + ? [ + `🆕 ${unmatchedInteractions} undocumented path${ + unmatchedInteractions > 1 ? 's' : '' + }`, + ] + : []), + ...(mismatchedEndpoints + ? [ + `âš ī¸ ${mismatchedEndpoints} mismatch${ + mismatchedEndpoints > 1 ? 'es' : '' + }`, + ] + : []), + ].join('\n'); +}; + +const getBreakagesRow = (breakage: CiRunDetails['completed'][number]) => { + return ` + - ${breakage.apiName} + ${breakage.comparison.results.map( + s => ` + - ${s.where} + ${'```'} + ${s.error} + ${'```'}`, + )}`; +}; + +const addSummaryLine = (items: any[] | number | undefined, label: string) => { + const length = Array.isArray(items) ? items.length : items; + if (!length) return ''; + let text = length === 1 ? `1 API` : `${length} APIs`; + text += ` had ${label}`; + return text; +}; + +export const generateCompareSummaryMarkdown = ( + commit: { sha: string }, + results: CiRunDetails, + options: { verbose: boolean }, +) => { + const anyCompletedHasWarning = results.completed.some( + s => s.warnings.length > 0, + ); + const anyCompletedHasCapture = results.completed.some(s => s.capture); + if ( + results.completed.length === 0 && + results.failed.length === 0 && + results.failed.length === 0 + ) { + return `No API changes detected for commit (${commit.sha})`; + } + const breakages = results.completed + .filter(s => s.comparison.results.some(e => !e.passed)) + .map(e => ({ + ...e, + comparison: { + ...e.comparison, + results: e.comparison.results.filter(f => !f.passed), + }, + })); + const successfullyCompletedCount = + results.completed.length - breakages.length; + return `### Summary for commit (${commit.sha}) + +${addSummaryLine(results.noop, 'no changes')} + +${addSummaryLine(breakages.length, 'breaking changes')} + +${addSummaryLine(successfullyCompletedCount, 'non-breaking changes')} + +${addSummaryLine(results.warning, 'warnings')} + +${ + results.completed.length > 0 + ? `### APIs with Changes + +<table> +<thead> +<tr> +<th>API</th> +<th>Changes</th> +<th>Rules</th> +${anyCompletedHasWarning ? '<th>Warnings</th>' : ''} +${anyCompletedHasCapture ? '<th>Tests</th>' : ''} +</tr> +</thead> +<tbody> +${results.completed + .map( + s => + `<tr> +<td> +${s.apiName} +</td> +<td> +${getOperationsText(s.comparison.groupedDiffs, { + webUrl: s.opticWebUrl, + verbose: options.verbose, + labelJoiner: ',\n', +})} +</td> +<td> +${getChecksLabel(s.comparison.results, results.severity)} +</td> + +${anyCompletedHasWarning ? `<td>${s.warnings.join('\n')}</td>` : ''} + +${ + anyCompletedHasCapture + ? ` +<td> +${ + s.capture + ? s.capture.success + ? s.capture.mismatchedEndpoints || s.capture.unmatchedInteractions + ? getCaptureIssuesLabel({ + unmatchedInteractions: s.capture.unmatchedInteractions, + mismatchedEndpoints: s.capture.mismatchedEndpoints, + }) + : `✅ ${s.capture.percentCovered}% coverage` + : '❌ Failed to run' + : '' +} +</td> +` + : '' +} +</tr>`, + ) + .join('\n')} +</tbody> +</table>` + : '' +} + +${ + results.failed.length > 0 + ? `### APIs with Errors + +<table> +<thead> +<tr> +<th>API</th> +<th>Error</th> +</tr> +</thead> +<tbody> +${results.failed + .map( + s => `<tr> +<td>${s.apiName}</td> +<td> + +${'```'} +${s.error} +${'```'} + +</td> +</tr>`, + ) + .join('\n')} +</tbody> +</table> +` + : '' +} + +${ + results.warning && results.warning.length + ? ` +### APIs with Warnings +<table> +<thead> +<tr> +<th>API</th> +<th>Warning</th> +</tr> +</thead> +<tbody> + ${results.warning + .map(e => `<tr><td>${e.apiName}</td><td>${e.warning}</td></tr>`) + .join('\n')} +</tbody> +</table>` + : '' +} +${ + breakages.length > 0 + ? ` +### Routes with Breakages + +${breakages.map(getBreakagesRow).join('\n')} +` + : '' +} +`; +}; diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index 4f13e2c25a..99853ed312 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,37 @@ # techdocs-cli-embedded-app +## 0.2.96-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog@1.19.1-next.1 + - @backstage/app-defaults@1.5.5-next.1 + - @backstage/cli@0.26.5-next.0 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-techdocs@1.10.5-next.1 + - @backstage/plugin-techdocs-react@1.2.4-next.1 + +## 0.2.96-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.19.1-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-techdocs@1.10.5-next.0 + - @backstage/cli@0.26.5-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-techdocs-react@1.2.4-next.0 + - @backstage/app-defaults@1.5.5-next.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/integration-react@1.1.26 + - @backstage/test-utils@1.5.5-next.0 + ## 0.2.95 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 2a90a90246..ce19840c56 100644 --- a/packages/techdocs-cli-embedded-app/package.json +++ b/packages/techdocs-cli-embedded-app/package.json @@ -1,6 +1,6 @@ { "name": "techdocs-cli-embedded-app", - "version": "0.2.95", + "version": "0.2.96-next.1", "private": true, "backstage": { "role": "frontend" diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 5f7d2d2ddc..3b2c544e7d 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,25 @@ # @techdocs/cli +## 1.8.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-techdocs-node@1.12.4-next.1 + +## 1.8.10-next.0 + +### Patch Changes + +- 1a0e009: Fix cookie endpoint mock for `serve` +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/plugin-techdocs-node@1.12.4-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + ## 1.8.9 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 7525e53afc..13eed9d249 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,7 +1,7 @@ { "name": "@techdocs/cli", "description": "Utility CLI for managing TechDocs sites in Backstage.", - "version": "1.8.9", + "version": "1.8.11-next.1", "publishConfig": { "access": "public" }, diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index 169886aaec..d28d0cac49 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/test-utils +## 1.5.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.5.4-next.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-react@0.4.22 + ## 1.5.4 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 70ff1206ee..2e46e3a9b0 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/test-utils", - "version": "1.5.4", + "version": "1.5.5-next.0", "description": "Utilities to test Backstage plugins and apps.", "backstage": { "role": "web-library" diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index 9f4007a2ac..20703a1d9d 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/theme +## 0.5.4-next.0 + +### Patch Changes + +- f1462df: Fixed bug where scrollbars don't pick up the theme when in dark mode + ## 0.5.3 ### Patch Changes diff --git a/packages/theme/package.json b/packages/theme/package.json index f1c4f00d8f..07eefe756f 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/theme", - "version": "0.5.3", + "version": "0.5.4-next.0", "description": "material-ui theme for use with Backstage.", "backstage": { "role": "web-library" diff --git a/plugins/README.md b/plugins/README.md index 385e71b6d2..346db0d786 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -1,6 +1,6 @@ # Plugins -Backstage is a single-page application composed of a set of plugins. This folder holds numerous plugins that are managed by this repository. +Backstage is a single-page application composed of a set of plugins. This folder holds numerous plugins that are managed by this repository. However, most plugins are [in the community plugins repo](https://github.com/backstage/community-plugins) - hop over there if you want to contribute! For more information about the plugin ecosystem, see the documentation here: @@ -10,6 +10,6 @@ You can also see the [Plugin Marketplace](https://backstage.io/plugins) for othe ## Suggesting a plugin -If you start developing a plugin that you aim to release as open source, we suggest that you create a [new Issue](https://github.com/backstage/backstage/issues/new?template=plugin_template.md). This helps the community know what plugins are in development. +If you start developing a plugin that you aim to release as open source, we suggest that you create a [new Issue on the community plugins repo](https://github.com/backstage/community-plugins/issues/new/choose). This helps the community know what plugins are in development. You can also use this process if you have an idea for a good plugin but you hope that someone else will pick up the work. diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 7883dab3fb..faac54a107 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-api-docs +## 0.11.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog@1.19.1-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## 0.11.5-next.0 + +### Patch Changes + +- 5d99272: Update local development dependencies. +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/plugin-catalog@1.19.1-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/plugin-permission-react@0.4.22 + ## 0.11.4 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index a686b952b5..c8acdf256f 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.11.4", + "version": "0.11.5-next.1", "description": "A Backstage plugin that helps represent API entities in the frontend", "backstage": { "role": "frontend-plugin" diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 8768724d13..2b51f92102 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-app-backend +## 0.3.66-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/config-loader@1.8.0 + - @backstage/backend-plugin-api@0.6.18-next.1 + - @backstage/plugin-app-node@0.1.18-next.1 + +## 0.3.66-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.8.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-app-node@0.1.18-next.0 + ## 0.3.65 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index d7a3c4ee72..fc4a6a8ded 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-app-backend", "description": "A Backstage backend plugin that serves the Backstage frontend app", - "version": "0.3.65", + "version": "0.3.66-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/app-node/CHANGELOG.md b/plugins/app-node/CHANGELOG.md index ad315d2581..dfc8e08cb9 100644 --- a/plugins/app-node/CHANGELOG.md +++ b/plugins/app-node/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-app-node +## 0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.8.0 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config-loader@1.8.0 + ## 0.1.17 ### Patch Changes diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json index ccb07f4d34..a38ed25171 100644 --- a/plugins/app-node/package.json +++ b/plugins/app-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-app-node", "description": "Node.js library for the app plugin", - "version": "0.1.17", + "version": "0.1.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/app-visualizer/CHANGELOG.md b/plugins/app-visualizer/CHANGELOG.md index 7d785cf830..c3e117a8a0 100644 --- a/plugins/app-visualizer/CHANGELOG.md +++ b/plugins/app-visualizer/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-app-visualizer +## 0.1.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + +## 0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.5-next.0 + ## 0.1.5 ### Patch Changes diff --git a/plugins/app-visualizer/package.json b/plugins/app-visualizer/package.json index b17ae9b9e4..0b2370054c 100644 --- a/plugins/app-visualizer/package.json +++ b/plugins/app-visualizer/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-visualizer", - "version": "0.1.5", + "version": "0.1.6-next.1", "description": "Visualizes the Backstage app structure", "backstage": { "role": "frontend-plugin" diff --git a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md index d2c8ddcbad..915c9b0f19 100644 --- a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-atlassian-provider +## 0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + ## 0.1.9 ### Patch Changes diff --git a/plugins/auth-backend-module-atlassian-provider/package.json b/plugins/auth-backend-module-atlassian-provider/package.json index 45ac854f0c..0f0c3eeda9 100644 --- a/plugins/auth-backend-module-atlassian-provider/package.json +++ b/plugins/auth-backend-module-atlassian-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-atlassian-provider", "description": "The atlassian-provider backend module for the auth plugin.", - "version": "0.1.9", + "version": "0.1.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md index 8280573fc1..0b2fa1faf0 100644 --- a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-auth-backend-module-aws-alb-provider +## 0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-backend@0.22.5-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.22.5-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/errors@1.2.4 + ## 0.1.9 ### Patch Changes diff --git a/plugins/auth-backend-module-aws-alb-provider/config.d.ts b/plugins/auth-backend-module-aws-alb-provider/config.d.ts new file mode 100644 index 0000000000..1978e5a4df --- /dev/null +++ b/plugins/auth-backend-module-aws-alb-provider/config.d.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface Config { + auth?: { + providers?: { + /** @visibility frontend */ + awsalb?: { + issuer?: string; + region: string; + }; + }; + }; +} diff --git a/plugins/auth-backend-module-aws-alb-provider/package.json b/plugins/auth-backend-module-aws-alb-provider/package.json index 08cc174e13..ae25459ccc 100644 --- a/plugins/auth-backend-module-aws-alb-provider/package.json +++ b/plugins/auth-backend-module-aws-alb-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-aws-alb-provider", "description": "The aws-alb provider module for the Backstage auth backend.", - "version": "0.1.9", + "version": "0.1.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md b/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md index 07d9ac0d68..0ed21e2749 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-auth-backend-module-azure-easyauth-provider +## 0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/errors@1.2.4 + ## 0.1.0 ### Minor Changes diff --git a/plugins/auth-backend-module-azure-easyauth-provider/package.json b/plugins/auth-backend-module-azure-easyauth-provider/package.json index 08d20ffb8e..e936f48f43 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/package.json +++ b/plugins/auth-backend-module-azure-easyauth-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-azure-easyauth-provider", - "version": "0.1.0", + "version": "0.1.1-next.1", "description": "The azure-easyauth-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md index 188ec6445f..4b414015cc 100644 --- a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-bitbucket-provider +## 0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/auth-backend-module-bitbucket-provider/package.json b/plugins/auth-backend-module-bitbucket-provider/package.json index 90279e0c49..ba1184ab0f 100644 --- a/plugins/auth-backend-module-bitbucket-provider/package.json +++ b/plugins/auth-backend-module-bitbucket-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-bitbucket-provider", - "version": "0.1.0", + "version": "0.1.1-next.1", "description": "The bitbucket-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md index 8ea04f1bd0..1aabb47fb9 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-auth-backend-module-cloudflare-access-provider +## 0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + ## 0.1.0 ### Minor Changes diff --git a/plugins/auth-backend-module-cloudflare-access-provider/package.json b/plugins/auth-backend-module-cloudflare-access-provider/package.json index a7b6aab15c..77f77052b5 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/package.json +++ b/plugins/auth-backend-module-cloudflare-access-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-cloudflare-access-provider", - "version": "0.1.0", + "version": "0.1.1-next.1", "description": "The cloudflare-access-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md index 9d600c169b..03576e316e 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-auth-backend-module-gcp-iap-provider +## 0.2.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.2.12 ### Patch Changes diff --git a/plugins/auth-backend-module-gcp-iap-provider/package.json b/plugins/auth-backend-module-gcp-iap-provider/package.json index 156732fcb5..1492ebaaf2 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/package.json +++ b/plugins/auth-backend-module-gcp-iap-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-gcp-iap-provider", "description": "A GCP IAP auth provider module for the Backstage auth backend", - "version": "0.2.12", + "version": "0.2.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-github-provider/CHANGELOG.md b/plugins/auth-backend-module-github-provider/CHANGELOG.md index 73a7f93cca..c9da8b5970 100644 --- a/plugins/auth-backend-module-github-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-github-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-github-provider +## 0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + ## 0.1.14 ### Patch Changes diff --git a/plugins/auth-backend-module-github-provider/config.d.ts b/plugins/auth-backend-module-github-provider/config.d.ts new file mode 100644 index 0000000000..e79711310d --- /dev/null +++ b/plugins/auth-backend-module-github-provider/config.d.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface Config { + auth?: { + providers?: { + /** @visibility frontend */ + github?: { + [authEnv: string]: { + clientId: string; + /** + * @visibility secret + */ + clientSecret: string; + callbackUrl?: string; + enterpriseInstanceUrl?: string; + }; + }; + }; + }; +} diff --git a/plugins/auth-backend-module-github-provider/package.json b/plugins/auth-backend-module-github-provider/package.json index 314cf23d2d..0e8db4d1a5 100644 --- a/plugins/auth-backend-module-github-provider/package.json +++ b/plugins/auth-backend-module-github-provider/package.json @@ -1,10 +1,10 @@ { "name": "@backstage/plugin-auth-backend-module-github-provider", + "version": "0.1.15-next.1", "description": "The github-provider backend module for the auth plugin.", - "version": "0.1.14", - "main": "src/index.ts", - "types": "src/index.ts", - "license": "Apache-2.0", + "backstage": { + "role": "backend-plugin-module" + }, "publishConfig": { "access": "public", "main": "dist/index.cjs.js", @@ -15,17 +15,21 @@ "url": "https://github.com/backstage/backstage", "directory": "plugins/auth-backend-module-github-provider" }, - "backstage": { - "role": "backend-plugin-module" - }, + "license": "Apache-2.0", + "main": "src/index.ts", + "types": "src/index.ts", + "files": [ + "dist", + "config.d.ts" + ], "scripts": { - "start": "backstage-cli package start", "build": "backstage-cli package build", - "lint": "backstage-cli package lint", - "test": "backstage-cli package test", "clean": "backstage-cli package clean", + "lint": "backstage-cli package lint", "prepack": "backstage-cli package prepack", - "postpack": "backstage-cli package postpack" + "postpack": "backstage-cli package postpack", + "start": "backstage-cli package start", + "test": "backstage-cli package test" }, "dependencies": { "@backstage/backend-plugin-api": "workspace:^", @@ -39,7 +43,5 @@ "@backstage/plugin-auth-backend": "workspace:^", "supertest": "^6.3.3" }, - "files": [ - "dist" - ] + "configSchema": "config.d.ts" } diff --git a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md index 93739aaf82..1f4f5ecbf9 100644 --- a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-gitlab-provider +## 0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + ## 0.1.14 ### Patch Changes diff --git a/plugins/auth-backend-module-gitlab-provider/package.json b/plugins/auth-backend-module-gitlab-provider/package.json index 0f0ec79453..5d8ee28020 100644 --- a/plugins/auth-backend-module-gitlab-provider/package.json +++ b/plugins/auth-backend-module-gitlab-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-gitlab-provider", "description": "The gitlab-provider backend module for the auth plugin.", - "version": "0.1.14", + "version": "0.1.15-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-google-provider/CHANGELOG.md b/plugins/auth-backend-module-google-provider/CHANGELOG.md index 3cf73dfada..9e7e4043a3 100644 --- a/plugins/auth-backend-module-google-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-google-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-google-provider +## 0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + ## 0.1.14 ### Patch Changes diff --git a/plugins/auth-backend-module-google-provider/package.json b/plugins/auth-backend-module-google-provider/package.json index ecfd4b5a14..a9b529504d 100644 --- a/plugins/auth-backend-module-google-provider/package.json +++ b/plugins/auth-backend-module-google-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-google-provider", "description": "A Google auth provider module for the Backstage auth backend", - "version": "0.1.14", + "version": "0.1.15-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-guest-provider/CHANGELOG.md b/plugins/auth-backend-module-guest-provider/CHANGELOG.md index 759bb14463..56c4e179b8 100644 --- a/plugins/auth-backend-module-guest-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-guest-provider/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-auth-backend-module-guest-provider +## 0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.4-next.0 + +### Patch Changes + +- 07d8cca: Error if used outside of a development environment without explicit allowance +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/errors@1.2.4 + ## 0.1.3 ### Patch Changes diff --git a/plugins/auth-backend-module-guest-provider/package.json b/plugins/auth-backend-module-guest-provider/package.json index 67d7598a8b..9676afb176 100644 --- a/plugins/auth-backend-module-guest-provider/package.json +++ b/plugins/auth-backend-module-guest-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-guest-provider", - "version": "0.1.3", + "version": "0.1.4-next.1", "description": "The guest-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md index 1865ee7fe1..14e4cef987 100644 --- a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-microsoft-provider +## 0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + ## 0.1.12 ### Patch Changes diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index 0cf43b6166..a8569903cc 100644 --- a/plugins/auth-backend-module-microsoft-provider/package.json +++ b/plugins/auth-backend-module-microsoft-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-microsoft-provider", "description": "The microsoft-provider backend module for the auth plugin.", - "version": "0.1.12", + "version": "0.1.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md index 029f3705f0..747d8fa317 100644 --- a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-oauth2-provider +## 0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + ## 0.1.14 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-provider/package.json b/plugins/auth-backend-module-oauth2-provider/package.json index ece072b69c..7cbc5929ae 100644 --- a/plugins/auth-backend-module-oauth2-provider/package.json +++ b/plugins/auth-backend-module-oauth2-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-provider", "description": "The oauth2-provider backend module for the auth plugin.", - "version": "0.1.14", + "version": "0.1.15-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md index 28c75592b0..acc41221f6 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-auth-backend-module-oauth2-proxy-provider +## 0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/errors@1.2.4 + ## 0.1.10 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/package.json b/plugins/auth-backend-module-oauth2-proxy-provider/package.json index 42b8e9d320..cbcef196a5 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/package.json +++ b/plugins/auth-backend-module-oauth2-proxy-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-proxy-provider", "description": "The oauth2-proxy-provider backend module for the auth plugin.", - "version": "0.1.10", + "version": "0.1.11-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md index 6b5a1195d2..6713b3c7b6 100644 --- a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-auth-backend-module-oidc-provider +## 0.1.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-backend@0.22.5-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.9-next.0 + +### Patch Changes + +- dd53bf3: Add nonce to authorize request to be added in ID token +- Updated dependencies + - @backstage/plugin-auth-backend@0.22.5-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + ## 0.1.8 ### Patch Changes diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index 34be8ef931..c8894fc070 100644 --- a/plugins/auth-backend-module-oidc-provider/package.json +++ b/plugins/auth-backend-module-oidc-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-oidc-provider", "description": "The oidc-provider backend module for the auth plugin.", - "version": "0.1.8", + "version": "0.1.9-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-okta-provider/CHANGELOG.md b/plugins/auth-backend-module-okta-provider/CHANGELOG.md index cba0e66ed7..43e434a711 100644 --- a/plugins/auth-backend-module-okta-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-okta-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-okta-provider +## 0.0.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.0.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + ## 0.0.10 ### Patch Changes diff --git a/plugins/auth-backend-module-okta-provider/package.json b/plugins/auth-backend-module-okta-provider/package.json index 595f99c933..3006544145 100644 --- a/plugins/auth-backend-module-okta-provider/package.json +++ b/plugins/auth-backend-module-okta-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-okta-provider", "description": "The okta-provider backend module for the auth plugin.", - "version": "0.0.10", + "version": "0.0.11-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md index e78fc901b7..a115289d0b 100644 --- a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-auth-backend-module-pinniped-provider +## 0.1.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + ## 0.1.11 ### Patch Changes diff --git a/plugins/auth-backend-module-pinniped-provider/package.json b/plugins/auth-backend-module-pinniped-provider/package.json index fe29cb2592..163c3f8e05 100644 --- a/plugins/auth-backend-module-pinniped-provider/package.json +++ b/plugins/auth-backend-module-pinniped-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-pinniped-provider", "description": "The pinniped-provider backend module for the auth plugin.", - "version": "0.1.11", + "version": "0.1.12-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md index 4ca95a085d..9245b6ded5 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-auth-backend-module-vmware-cloud-provider +## 0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + ## 0.1.9 ### Patch Changes diff --git a/plugins/auth-backend-module-vmware-cloud-provider/package.json b/plugins/auth-backend-module-vmware-cloud-provider/package.json index 71813d653f..6054f63ea1 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/package.json +++ b/plugins/auth-backend-module-vmware-cloud-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-vmware-cloud-provider", - "version": "0.1.9", + "version": "0.1.10-next.1", "description": "The vmware-cloud-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 77e4ddd20a..aa33d39713 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,59 @@ # @backstage/plugin-auth-backend +## 0.22.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.10-next.1 + - @backstage/plugin-auth-backend-module-oidc-provider@0.1.9-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.1.10-next.1 + - @backstage/plugin-auth-backend-module-bitbucket-provider@0.1.1-next.1 + - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.1.1-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.1.15-next.1 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.15-next.1 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.1.13-next.1 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.1.15-next.1 + - @backstage/plugin-auth-backend-module-okta-provider@0.0.11-next.1 + - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.1.1-next.1 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.13-next.1 + - @backstage/plugin-auth-backend-module-google-provider@0.1.15-next.1 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.11-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.22.5-next.0 + +### Patch Changes + +- ea9262b: Allow overriding default ownership resolving with the new `AuthOwnershipResolutionExtensionPoint` +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-backend-module-oidc-provider@0.1.9-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.1.1-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.1.10-next.0 + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.10-next.0 + - @backstage/plugin-auth-backend-module-bitbucket-provider@0.1.1-next.0 + - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.1.1-next.0 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.13-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.1.15-next.0 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.15-next.0 + - @backstage/plugin-auth-backend-module-google-provider@0.1.15-next.0 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.1.13-next.0 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.1.15-next.0 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.11-next.0 + - @backstage/plugin-auth-backend-module-okta-provider@0.0.11-next.0 + ## 0.22.4 ### Patch Changes diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index f190b17ea1..d15399d279 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -3,6 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { AuthOwnershipResolver } from '@backstage/plugin-auth-node'; import { AuthProviderConfig as AuthProviderConfig_2 } from '@backstage/plugin-auth-node'; import { AuthProviderFactory as AuthProviderFactory_2 } from '@backstage/plugin-auth-node'; import { AuthProviderRouteHandlers as AuthProviderRouteHandlers_2 } from '@backstage/plugin-auth-node'; @@ -665,6 +666,8 @@ export interface RouterOptions { // (undocumented) logger: LoggerService; // (undocumented) + ownershipResolver?: AuthOwnershipResolver; + // (undocumented) providerFactories?: ProviderFactories; // (undocumented) tokenFactoryAlgorithm?: string; diff --git a/plugins/auth-backend/config.d.ts b/plugins/auth-backend/config.d.ts index f0ceaa4224..a7ea18ed1d 100644 --- a/plugins/auth-backend/config.d.ts +++ b/plugins/auth-backend/config.d.ts @@ -89,29 +89,6 @@ export interface Config { * @additionalProperties true */ providers?: { - /** @visibility frontend */ - google?: { - [authEnv: string]: { - clientId: string; - /** - * @visibility secret - */ - clientSecret: string; - callbackUrl?: string; - }; - }; - /** @visibility frontend */ - github?: { - [authEnv: string]: { - clientId: string; - /** - * @visibility secret - */ - clientSecret: string; - callbackUrl?: string; - enterpriseInstanceUrl?: string; - }; - }; /** @visibility frontend */ saml?: { entryPoint: string; @@ -137,20 +114,6 @@ export interface Config { acceptedClockSkewMs?: number; }; /** @visibility frontend */ - oauth2?: { - [authEnv: string]: { - clientId: string; - /** - * @visibility secret - */ - clientSecret: string; - authorizationUrl: string; - tokenUrl: string; - scope?: string; - disableRefresh?: boolean; - }; - }; - /** @visibility frontend */ auth0?: { [authEnv: string]: { clientId: string; @@ -177,11 +140,6 @@ export interface Config { callbackUrl?: string; }; }; - /** @visibility frontend */ - awsalb?: { - iss?: string; - region: string; - }; /** * The backstage token expiration. */ diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 91b28063d4..372b8f8d4b 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.22.4", + "version": "0.22.5-next.1", "description": "A Backstage backend plugin that handles authentication", "backstage": { "role": "backend-plugin" diff --git a/plugins/auth-backend/src/authPlugin.ts b/plugins/auth-backend/src/authPlugin.ts index f531ffabc6..7687462d32 100644 --- a/plugins/auth-backend/src/authPlugin.ts +++ b/plugins/auth-backend/src/authPlugin.ts @@ -19,6 +19,8 @@ import { createBackendPlugin, } from '@backstage/backend-plugin-api'; import { + authOwnershipResolutionExtensionPoint, + AuthOwnershipResolver, AuthProviderFactory, authProvidersExtensionPoint, } from '@backstage/plugin-auth-node'; @@ -34,6 +36,7 @@ export const authPlugin = createBackendPlugin({ pluginId: 'auth', register(reg) { const providers = new Map<string, AuthProviderFactory>(); + let ownershipResolver: AuthOwnershipResolver | undefined = undefined; reg.registerExtensionPoint(authProvidersExtensionPoint, { registerProvider({ providerId, factory }) { @@ -46,6 +49,15 @@ export const authPlugin = createBackendPlugin({ }, }); + reg.registerExtensionPoint(authOwnershipResolutionExtensionPoint, { + setAuthOwnershipResolver(resolver) { + if (ownershipResolver) { + throw new Error('Auth ownership resolver is already set'); + } + ownershipResolver = resolver; + }, + }); + reg.registerInit({ deps: { httpRouter: coreServices.httpRouter, @@ -80,6 +92,7 @@ export const authPlugin = createBackendPlugin({ catalogApi, providerFactories: Object.fromEntries(providers), disableDefaultProviderFactories: true, + ownershipResolver, }); httpRouter.addAuthPolicy({ path: '/', diff --git a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts index 4f9673ac79..c81ac4c355 100644 --- a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts +++ b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts @@ -31,12 +31,13 @@ import { LoggerService, } from '@backstage/backend-plugin-api'; import { TokenIssuer } from '../../identity/types'; -import { CatalogIdentityClient } from '../catalog'; import { + AuthOwnershipResolver, AuthResolverCatalogUserQuery, AuthResolverContext, TokenParams, } from '@backstage/plugin-auth-node'; +import { CatalogIdentityClient } from '../catalog'; /** * Uses the default ownership resolution logic to return an array @@ -69,6 +70,7 @@ export class CatalogAuthResolverContext implements AuthResolverContext { discovery: DiscoveryService; auth: AuthService; httpAuth: HttpAuthService; + ownershipResolver?: AuthOwnershipResolver; }): CatalogAuthResolverContext { const catalogIdentityClient = new CatalogIdentityClient({ catalogApi: options.catalogApi, @@ -84,6 +86,7 @@ export class CatalogAuthResolverContext implements AuthResolverContext { catalogIdentityClient, options.catalogApi, options.auth, + options.ownershipResolver, ); } @@ -93,6 +96,7 @@ export class CatalogAuthResolverContext implements AuthResolverContext { public readonly catalogIdentityClient: CatalogIdentityClient, private readonly catalogApi: CatalogApi, private readonly auth: AuthService, + private readonly ownershipResolver?: AuthOwnershipResolver, ) {} async issueToken(params: TokenParams) { @@ -160,12 +164,19 @@ export class CatalogAuthResolverContext implements AuthResolverContext { async signInWithCatalogUser(query: AuthResolverCatalogUserQuery) { const { entity } = await this.findCatalogUser(query); - const ownershipRefs = getDefaultOwnershipEntityRefs(entity); + let ent: string[]; + if (this.ownershipResolver) { + const { ownershipEntityRefs } = + await this.ownershipResolver.resolveOwnershipEntityRefs(entity); + ent = ownershipEntityRefs; + } else { + ent = getDefaultOwnershipEntityRefs(entity); + } const token = await this.tokenIssuer.issueToken({ claims: { sub: stringifyEntityRef(entity), - ent: ownershipRefs, + ent, }, }); return { token }; diff --git a/plugins/auth-backend/src/providers/router.ts b/plugins/auth-backend/src/providers/router.ts index 8c927f52c6..d84133685c 100644 --- a/plugins/auth-backend/src/providers/router.ts +++ b/plugins/auth-backend/src/providers/router.ts @@ -25,8 +25,11 @@ import { } from '@backstage/backend-plugin-api'; import { CatalogApi, CatalogClient } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; -import { NotFoundError, assertError } from '@backstage/errors'; -import { AuthProviderFactory } from '@backstage/plugin-auth-node'; +import { assertError, NotFoundError } from '@backstage/errors'; +import { + AuthOwnershipResolver, + AuthProviderFactory, +} from '@backstage/plugin-auth-node'; import express from 'express'; import Router from 'express-promise-router'; import { Minimatch } from 'minimatch'; @@ -49,6 +52,7 @@ export function bindProviderRouters( httpAuth: HttpAuthService; tokenManager: TokenManager; tokenIssuer: TokenIssuer; + ownershipResolver?: AuthOwnershipResolver; catalogApi?: CatalogApi; }, ) { @@ -64,6 +68,7 @@ export function bindProviderRouters( tokenManager, tokenIssuer, catalogApi, + ownershipResolver, } = options; const providersConfig = config.getOptionalConfig('auth.providers'); @@ -95,6 +100,7 @@ export function bindProviderRouters( discovery, auth, httpAuth, + ownershipResolver, }), }); diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index f1487d07a2..11c150aa33 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -23,15 +23,16 @@ import { LoggerService, } from '@backstage/backend-plugin-api'; import { defaultAuthProviderFactories } from '../providers'; +import { AuthOwnershipResolver } from '@backstage/plugin-auth-node'; import { + createLegacyAuthAdapters, PluginDatabaseManager, PluginEndpointDiscovery, TokenManager, - createLegacyAuthAdapters, } from '@backstage/backend-common'; import { NotFoundError } from '@backstage/errors'; import { CatalogApi } from '@backstage/catalog-client'; -import { bindOidcRouter, TokenFactory, KeyStores } from '../identity'; +import { bindOidcRouter, KeyStores, TokenFactory } from '../identity'; import session from 'express-session'; import connectSessionKnex from 'connect-session-knex'; import passport from 'passport'; @@ -41,7 +42,7 @@ import { TokenIssuer } from '../identity/types'; import { StaticTokenIssuer } from '../identity/StaticTokenIssuer'; import { StaticKeyStore } from '../identity/StaticKeyStore'; import { Config } from '@backstage/config'; -import { ProviderFactories, bindProviderRouters } from '../providers/router'; +import { bindProviderRouters, ProviderFactories } from '../providers/router'; /** @public */ export interface RouterOptions { @@ -56,6 +57,7 @@ export interface RouterOptions { providerFactories?: ProviderFactories; disableDefaultProviderFactories?: boolean; catalogApi?: CatalogApi; + ownershipResolver?: AuthOwnershipResolver; } /** @public */ diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index 563a7eda9c..af4318227d 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-auth-node +## 0.4.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.4.13-next.0 + +### Patch Changes + +- ea9262b: Allow overriding default ownership resolving with the new `AuthOwnershipResolutionExtensionPoint` +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.4.12 ### Patch Changes diff --git a/plugins/auth-node/api-report.md b/plugins/auth-node/api-report.md index e79facc7f9..6a43d3b127 100644 --- a/plugins/auth-node/api-report.md +++ b/plugins/auth-node/api-report.md @@ -21,6 +21,23 @@ import { Strategy } from 'passport'; import { ZodSchema } from 'zod'; import { ZodTypeDef } from 'zod'; +// @public (undocumented) +export interface AuthOwnershipResolutionExtensionPoint { + // (undocumented) + setAuthOwnershipResolver(ownershipResolver: AuthOwnershipResolver): void; +} + +// @public (undocumented) +export const authOwnershipResolutionExtensionPoint: ExtensionPoint<AuthOwnershipResolutionExtensionPoint>; + +// @public +export interface AuthOwnershipResolver { + // (undocumented) + resolveOwnershipEntityRefs(entity: Entity): Promise<{ + ownershipEntityRefs: string[]; + }>; +} + // @public @deprecated (undocumented) export type AuthProviderConfig = { baseUrl: string; diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index 183388e432..ba05caaad4 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.4.12", + "version": "0.4.13-next.1", "backstage": { "role": "node-library" }, diff --git a/plugins/catalog-backend/src/ingestion/types.ts b/plugins/auth-node/src/extensions/AuthOwnershipResolutionExtensionPoint.ts similarity index 53% rename from plugins/catalog-backend/src/ingestion/types.ts rename to plugins/auth-node/src/extensions/AuthOwnershipResolutionExtensionPoint.ts index 6ca0ab259b..d6235a75e2 100644 --- a/plugins/catalog-backend/src/ingestion/types.ts +++ b/plugins/auth-node/src/extensions/AuthOwnershipResolutionExtensionPoint.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,21 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import { - AnalyzeLocationRequest, - AnalyzeLocationResponse, -} from '@backstage/plugin-catalog-common'; +import { AuthOwnershipResolver } from '../types'; +import { createExtensionPoint } from '@backstage/backend-plugin-api'; /** @public */ -export type LocationAnalyzer = { - /** - * Generates an entity configuration for given git repository. It's used for - * importing new component to the backstage app. - * - * @param location - Git repository to analyze and generate config for. - */ - analyzeLocation( - location: AnalyzeLocationRequest, - ): Promise<AnalyzeLocationResponse>; -}; +export interface AuthOwnershipResolutionExtensionPoint { + setAuthOwnershipResolver(ownershipResolver: AuthOwnershipResolver): void; +} + +/** @public */ +export const authOwnershipResolutionExtensionPoint = + createExtensionPoint<AuthOwnershipResolutionExtensionPoint>({ + id: 'auth.ownershipResolution', + }); diff --git a/plugins/auth-node/src/extensions/index.ts b/plugins/auth-node/src/extensions/index.ts index 43e1751685..efca1623b3 100644 --- a/plugins/auth-node/src/extensions/index.ts +++ b/plugins/auth-node/src/extensions/index.ts @@ -19,3 +19,8 @@ export { type AuthProviderRegistrationOptions, type AuthProvidersExtensionPoint, } from './AuthProvidersExtensionPoint'; + +export { + authOwnershipResolutionExtensionPoint, + type AuthOwnershipResolutionExtensionPoint, +} from './AuthOwnershipResolutionExtensionPoint'; diff --git a/plugins/auth-node/src/index.ts b/plugins/auth-node/src/index.ts index 8b35aaa1e1..39063bf7c2 100644 --- a/plugins/auth-node/src/index.ts +++ b/plugins/auth-node/src/index.ts @@ -43,5 +43,6 @@ export type { SignInInfo, SignInResolver, TokenParams, + AuthOwnershipResolver, } from './types'; export { tokenTypes } from './types'; diff --git a/plugins/auth-node/src/types.ts b/plugins/auth-node/src/types.ts index 39172437ec..845fd7b0cb 100644 --- a/plugins/auth-node/src/types.ts +++ b/plugins/auth-node/src/types.ts @@ -163,6 +163,17 @@ export type AuthResolverContext = { ): Promise<BackstageSignInResult>; }; +/** + * Resolver interface for resolving the ownership entity references for entity + * + * @public + */ +export interface AuthOwnershipResolver { + resolveOwnershipEntityRefs( + entity: Entity, + ): Promise<{ ownershipEntityRefs: string[] }>; +} + /** * Any Auth provider needs to implement this interface which handles the routes in the * auth backend. Any auth API requests from the frontend reaches these methods. diff --git a/plugins/auth-react/CHANGELOG.md b/plugins/auth-react/CHANGELOG.md index eb21584bbc..ebd567184f 100644 --- a/plugins/auth-react/CHANGELOG.md +++ b/plugins/auth-react/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-auth-react +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## 0.1.1-next.0 + +### Patch Changes + +- c297afd: When using `CookieAuthRefreshProvider` or `useCookieAuthRefresh`, a 404 response from the cookie endpoint will now be treated as if cookie auth is disabled and is not needed. +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + ## 0.1.0 ### Minor Changes diff --git a/plugins/auth-react/package.json b/plugins/auth-react/package.json index 625798ee6e..c223b4c40f 100644 --- a/plugins/auth-react/package.json +++ b/plugins/auth-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-react", - "version": "0.1.0", + "version": "0.1.2-next.1", "description": "Web library for the auth plugin", "backstage": { "role": "web-library" diff --git a/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.test.tsx b/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.test.tsx index df5c833953..422f392e93 100644 --- a/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.test.tsx +++ b/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.test.tsx @@ -217,6 +217,39 @@ describe('useCookieAuthRefresh', () => { ); }); + it('should handle 404 as disabled cookie auth', async () => { + const { result } = renderHook( + () => useCookieAuthRefresh({ pluginId: 'techdocs' }), + { + wrapper: ({ children }) => ( + <TestApiProvider + apis={[ + [ + fetchApiRef, + { + fetch: jest.fn().mockResolvedValue({ + ok: false, + status: 404, + }), + }, + ], + [discoveryApiRef, discoveryApiMock], + ]} + > + {children} + </TestApiProvider> + ), + }, + ); + + await waitFor(() => + expect(result.current).toEqual({ + status: 'success', + data: { expiresAt: expect.any(Date) }, + }), + ); + }); + it('should call the api to get the cookie and use it', async () => { const { result } = renderHook( () => useCookieAuthRefresh({ pluginId: 'techdocs' }), diff --git a/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.tsx b/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.tsx index 244491f7e6..a6860ad185 100644 --- a/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.tsx +++ b/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.tsx @@ -24,6 +24,7 @@ import { useAsync, useMountEffect } from '@react-hookz/web'; import { ResponseError } from '@backstage/errors'; const COOKIE_PATH = '/.backstage/auth/v1/cookie'; +const ONE_YEAR_MS = 365 * 24 * 3600_000; /** * @public @@ -54,6 +55,14 @@ export function useCookieAuthRefresh(options: { credentials: 'include', }); if (!response.ok) { + // If we get a 404 from the cookie endpoint we assume that it does not + // exist and cookie auth is not needed. For all active tabs we don't + // schedule another refresh for the forseeable future, but new tabs will + // still check if cookie auth has been added to the deployment. + // TODO(Rugvip): Once the legacy backend system is no longer supported we should remove this check + if (response.status === 404) { + return { expiresAt: new Date(Date.now() + ONE_YEAR_MS) }; + } throw await ResponseError.fromResponse(response); } const data = await response.json(); diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index b371ddc2af..be6e743eb9 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.3.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-kubernetes-common@0.7.6-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/integration-aws-node@0.1.12 + ## 0.3.12 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index 3b8fec8b1f..fbc4122fe2 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", - "version": "0.3.12", + "version": "0.3.13-next.1", "description": "A Backstage catalog backend module that helps integrate towards AWS", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index e9a3e83a5e..5b7751dc03 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.1.38-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.38-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.10.0 + ## 0.1.37 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index d1a7f9e851..4df7824fa7 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-azure", - "version": "0.1.37", + "version": "0.1.38-next.1", "description": "A Backstage catalog backend module that helps integrate towards Azure", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md index 3a7b92c12e..449c016837 100644 --- a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-catalog-backend-module-backstage-openapi +## 0.2.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + - @backstage/backend-openapi-utils@0.1.11-next.1 + +## 0.2.1-next.0 + +### Patch Changes + +- f3f0281: Fix incorrect dependency import. +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-openapi-utils@0.1.11-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + ## 0.2.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-backstage-openapi/package.json b/plugins/catalog-backend-module-backstage-openapi/package.json index 12933b5997..86c7bb02e7 100644 --- a/plugins/catalog-backend-module-backstage-openapi/package.json +++ b/plugins/catalog-backend-module-backstage-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-backstage-openapi", - "version": "0.2.0", + "version": "0.2.1-next.1", "backstage": { "role": "backend-plugin-module" }, diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index 7040c696e8..a40ceb8254 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.2.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.2.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.10.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.18 + - @backstage/plugin-events-node@0.3.4-next.0 + ## 0.2.4 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index b32b03c63f..f6b4620671 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", - "version": "0.2.4", + "version": "0.2.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index ecb2713666..153494a358 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.1.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.32-next.0 + +### Patch Changes + +- 062ffb1: Allow skipping archived repositories (`skipArchivedRepos` flag) on Bitbucket. +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + ## 0.1.31 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 3247b326a4..e46d0107a5 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-server", - "version": "0.1.31", + "version": "0.1.32-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-gcp/CHANGELOG.md b/plugins/catalog-backend-module-gcp/CHANGELOG.md index 93f2e3eec5..18299d760a 100644 --- a/plugins/catalog-backend-module-gcp/CHANGELOG.md +++ b/plugins/catalog-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-catalog-backend-module-gcp +## 0.1.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-kubernetes-common@0.7.6-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + ## 0.1.18 ### Patch Changes diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index 4769adfdb9..c58c014b0e 100644 --- a/plugins/catalog-backend-module-gcp/package.json +++ b/plugins/catalog-backend-module-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gcp", - "version": "0.1.18", + "version": "0.1.19-next.1", "description": "A Backstage catalog backend module that helps integrate towards GCP", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index ce41f869f5..8b5e5edd96 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.1.35-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.35-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + ## 0.1.34 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 03c96bab63..c436a07263 100644 --- a/plugins/catalog-backend-module-gerrit/package.json +++ b/plugins/catalog-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gerrit", - "version": "0.1.34", + "version": "0.1.35-next.1", "backstage": { "role": "backend-plugin-module" }, diff --git a/plugins/catalog-backend-module-github-org/CHANGELOG.md b/plugins/catalog-backend-module-github-org/CHANGELOG.md index d0163c07c7..b1fd9b17d7 100644 --- a/plugins/catalog-backend-module-github-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-github-org/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-catalog-backend-module-github-org +## 0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-backend-module-github@0.6.1-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-backend-module-github@0.6.1-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-events-node@0.3.4-next.0 + ## 0.1.12 ### Patch Changes diff --git a/plugins/catalog-backend-module-github-org/package.json b/plugins/catalog-backend-module-github-org/package.json index 0abc15c795..2838396c88 100644 --- a/plugins/catalog-backend-module-github-org/package.json +++ b/plugins/catalog-backend-module-github-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github-org", - "version": "0.1.12", + "version": "0.1.13-next.1", "description": "The github-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index 471397861a..5bb58f25c0 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,34 @@ # @backstage/plugin-catalog-backend-module-github +## 0.6.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-catalog-backend@1.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.6.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.22.0-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.10.0 + - @backstage/plugin-events-node@0.3.4-next.0 + ## 0.6.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index 2067ec340a..47a32566af 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github", - "version": "0.6.0", + "version": "0.6.1-next.1", "description": "A Backstage catalog backend module that helps integrate towards GitHub", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/catalog-backend-module-github/src/module/githubCatalogModule.test.ts b/plugins/catalog-backend-module-github/src/module/githubCatalogModule.test.ts index 0f466fefb7..24ee7f7108 100644 --- a/plugins/catalog-backend-module-github/src/module/githubCatalogModule.test.ts +++ b/plugins/catalog-backend-module-github/src/module/githubCatalogModule.test.ts @@ -37,7 +37,8 @@ describe('githubCatalogModule', () => { }; const analysisExtensionPoint = { - addLocationAnalyzer: jest.fn(), + setLocationAnalyzer: jest.fn(), + addScmLocationAnalyzer: jest.fn(), }; const runner = jest.fn(); @@ -81,7 +82,7 @@ describe('githubCatalogModule', () => { 'github-provider:default', ); expect(runner).not.toHaveBeenCalled(); - expect(analysisExtensionPoint.addLocationAnalyzer).toHaveBeenCalledWith( + expect(analysisExtensionPoint.addScmLocationAnalyzer).toHaveBeenCalledWith( expect.any(GithubLocationAnalyzer), ); }); diff --git a/plugins/catalog-backend-module-github/src/module/githubCatalogModule.ts b/plugins/catalog-backend-module-github/src/module/githubCatalogModule.ts index 0b46e1a023..809a1a5094 100644 --- a/plugins/catalog-backend-module-github/src/module/githubCatalogModule.ts +++ b/plugins/catalog-backend-module-github/src/module/githubCatalogModule.ts @@ -56,7 +56,7 @@ export const githubCatalogModule = createBackendModule({ discovery, auth, }) { - analyzers.addLocationAnalyzer( + analyzers.addScmLocationAnalyzer( new GithubLocationAnalyzer({ discovery, config, diff --git a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md new file mode 100644 index 0000000000..fa0b9f2a24 --- /dev/null +++ b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md @@ -0,0 +1,24 @@ +# @backstage/plugin-catalog-backend-module-gitlab-org + +## 0.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-catalog-backend-module-gitlab@0.3.15-next.3 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.0.1-next.0 + +### Patch Changes + +- a70377d: Added a new `catalog-backend-module-gitlab-org` module which adds the `GitlabOrgDiscoveryEntityProvider` to the catalog's providers using the new backend system. +- Updated dependencies + - @backstage/plugin-catalog-backend-module-gitlab@0.3.15-next.2 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-events-node@0.3.4-next.0 diff --git a/plugins/catalog-backend-module-gitlab-org/package.json b/plugins/catalog-backend-module-gitlab-org/package.json index 7d92fda325..c2d524d9d8 100644 --- a/plugins/catalog-backend-module-gitlab-org/package.json +++ b/plugins/catalog-backend-module-gitlab-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab-org", - "version": "0.0.0", + "version": "0.0.1-next.1", "description": "The gitlab-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index 92f0ea7f21..5b36f2570b 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.3.15-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.3.15-next.2 + +### Patch Changes + +- a70377d: Added events support for `GitlabDiscoveryEntityProvider` and `GitlabOrgDiscoveryEntityProvider`. +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.10.0 + - @backstage/plugin-events-node@0.3.4-next.0 + ## 0.3.15 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index 99fdde8346..d0d3c876ae 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -72,5 +72,5 @@ ] } }, - "version": "0.3.15-next.1" + "version": "0.3.15-next.3" } diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index b922cf5f4f..2f4505abba 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md +++ b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md @@ -1,5 +1,34 @@ # @backstage/plugin-catalog-backend-module-incremental-ingestion +## 0.4.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-catalog-backend@1.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.4.23-next.0 + +### Patch Changes + +- 8c1ab9e: Fix plugin/incremental-ingestion 'Maximum call stack size exceeded' error when ingest large entities. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.22.0-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-events-node@0.3.4-next.0 + - @backstage/plugin-permission-common@0.7.13 + ## 0.4.22 ### Patch Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index c8cc5456b0..6c9f930d33 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/package.json +++ b/plugins/catalog-backend-module-incremental-ingestion/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-incremental-ingestion", - "version": "0.4.22", + "version": "0.4.23-next.1", "description": "An entity provider for streaming large asset sources into the catalog", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/database/IncrementalIngestionDatabaseManager.ts b/plugins/catalog-backend-module-incremental-ingestion/src/database/IncrementalIngestionDatabaseManager.ts index e47954f47e..818a93fc6d 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/database/IncrementalIngestionDatabaseManager.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/database/IncrementalIngestionDatabaseManager.ts @@ -336,11 +336,9 @@ export class IncrementalIngestionDatabaseManager { .join('ingestions', 'ingestions.id', 'ingestion_marks.ingestion_id') .where('ingestions.id', previousIngestion.id); - removed.push( - ...stale.map(e => { - return { entityRef: e.ref }; - }), - ); + for (const entityRef of stale) { + removed.push({ entityRef: entityRef.ref }); + } } return { total, removed }; diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.ts b/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.ts index 0262dfd418..7a7b1d681d 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.ts @@ -322,7 +322,9 @@ export class IncrementalIngestionEngine } } if (doRemoval) { - removed.push(...result.removed); + for (const entityRef of result.removed) { + removed.push(entityRef); + } } } diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index b8fad6f530..10a774ffad 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.5.34-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + +## 0.5.34-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.5.33 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index e95089c2dd..82558bc6d8 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", - "version": "0.5.33", + "version": "0.5.34-next.1", "description": "A Backstage catalog backend module that helps integrate towards LDAP", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index 5db50bd642..8b7dcb9c80 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.5.26-next.1 + +### Patch Changes + +- 49eab29: Fixed disabling of user photo fetching. Previously, the config value wasn't propagated properly, so user photos was still being fetched despite disabled by config. +- 6e370e6: Handle fetching huge amounts of users from Azure without crashing +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.5.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + ## 0.5.25 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/README.md b/plugins/catalog-backend-module-msgraph/README.md index e3ed302fa9..7ee416ecd4 100644 --- a/plugins/catalog-backend-module-msgraph/README.md +++ b/plugins/catalog-backend-module-msgraph/README.md @@ -54,6 +54,8 @@ catalog: # and for the syntax https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter # This and userGroupMemberFilter are mutually exclusive, only one can be specified filter: accountEnabled eq true and userType eq 'member' + # Set to false to not load user photos. + loadPhotos: true # See https://docs.microsoft.com/en-us/graph/api/resources/schemaextension?view=graph-rest-1.0 select: ['id', 'displayName', 'description'] # Optional configuration block diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index f8f203849e..7a89b0c507 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", - "version": "0.5.25", + "version": "0.5.26-next.1", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts index b1dfc5a148..9f4b9ee08c 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts @@ -955,6 +955,43 @@ describe('read microsoft graph', () => { ); }); + it('should ignore loading photos if loadPhotos is false', async () => { + client.getOrganization.mockResolvedValue(getExampleOrg()); + + client.getUsers.mockImplementation(getExampleUsers); + client.getUserPhotoWithSizeLimit.mockResolvedValue( + 'data:image/jpeg;base64,...', + ); + + client.getGroups.mockImplementation(getExampleGroups); + client.getGroupMembers.mockImplementation(getExampleGroupMembers); + client.getGroupPhotoWithSizeLimit.mockResolvedValue( + 'data:image/jpeg;base64,...', + ); + + await readMicrosoftGraphOrg(client, 'tenantid', { + logger: getVoidLogger(), + loadUserPhotos: false, + }); + + expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledTimes(0); + + expect(client.getUsers).toHaveBeenCalledTimes(1); + expect(client.getUsers).toHaveBeenCalledWith( + { + top: 999, + }, + undefined, + ); + expect(client.getGroups).toHaveBeenCalledTimes(1); + expect(client.getGroups).toHaveBeenCalledWith( + { + top: 999, + }, + undefined, + ); + }); + it('should read users with userSelect', async () => { client.getOrganization.mockResolvedValue({ id: 'tenantid', @@ -1027,5 +1064,51 @@ describe('read microsoft graph', () => { ); expect(client.getUserPhotoWithSizeLimit).toHaveBeenCalledTimes(1); }); + + it('should handle loading huge amounts of users', async () => { + client.getOrganization.mockResolvedValue(getExampleOrg()); + + const userCount = 200_000; + + async function* getHugeAmountsOfExampleUsers() { + for (let i = 0; i < userCount; ++i) { + yield { + id: `userid-${i}`, + displayName: 'User Name', + mail: 'user.name@example.com', + }; + } + } + + client.getUsers.mockImplementation(getHugeAmountsOfExampleUsers); + + client.getGroups.mockImplementation(getExampleGroups); + client.getGroupMembers.mockImplementation(getExampleGroupMembers); + client.getGroupPhotoWithSizeLimit.mockResolvedValue( + 'data:image/jpeg;base64,...', + ); + + const { users } = await readMicrosoftGraphOrg(client, 'tenantid', { + logger: getVoidLogger(), + loadUserPhotos: false, + }); + + expect(users.length).toBe(userCount); + + expect(client.getUsers).toHaveBeenCalledTimes(1); + expect(client.getUsers).toHaveBeenCalledWith( + { + top: 999, + }, + undefined, + ); + expect(client.getGroups).toHaveBeenCalledTimes(1); + expect(client.getGroups).toHaveBeenCalledWith( + { + top: 999, + }, + undefined, + ); + }); }); }); diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts index 0f3d81c224..2ca21fca9c 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts @@ -384,7 +384,7 @@ export async function readMicrosoftGraphOrg( logger: LoggerService; }, ): Promise<{ users: UserEntity[]; groups: GroupEntity[] }> { - const users: UserEntity[] = []; + let users: UserEntity[] = []; if (options.userGroupMemberFilter || options.userGroupMemberSearch) { const { users: usersInGroups } = await readMicrosoftGraphUsersInGroups( @@ -401,7 +401,7 @@ export async function readMicrosoftGraphOrg( logger: options.logger, }, ); - users.push(...usersInGroups); + users = usersInGroups; } else { const { users: usersWithFilter } = await readMicrosoftGraphUsers(client, { queryMode: options.queryMode, @@ -412,7 +412,7 @@ export async function readMicrosoftGraphOrg( transformer: options.userTransformer, logger: options.logger, }); - users.push(...usersWithFilter); + users = usersWithFilter; } const { groups, rootGroup, groupMember, groupMemberOf } = await readMicrosoftGraphGroups(client, tenantId, { diff --git a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts index 9c804216e4..3c8b903e69 100644 --- a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts @@ -310,6 +310,7 @@ export class MicrosoftGraphOrgEntityProvider implements EntityProvider { userExpand: provider.userExpand, userFilter: provider.userFilter, userSelect: provider.userSelect, + loadUserPhotos: provider.loadUserPhotos, userGroupMemberFilter: provider.userGroupMemberFilter, userGroupMemberSearch: provider.userGroupMemberSearch, groupExpand: provider.groupExpand, diff --git a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgReaderProcessor.ts b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgReaderProcessor.ts index 570959a381..c058286cb2 100644 --- a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgReaderProcessor.ts +++ b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgReaderProcessor.ts @@ -112,6 +112,7 @@ export class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor { { userExpand: provider.userExpand, userFilter: provider.userFilter, + loadUserPhotos: provider.loadUserPhotos, userGroupMemberFilter: provider.userGroupMemberFilter, userGroupMemberSearch: provider.userGroupMemberSearch, groupExpand: provider.groupExpand, diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index 00b73a76d3..f7303d298e 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.1.36-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-catalog-backend@1.22.0-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.36-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.22.0-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.10.0 + - @backstage/types@1.1.1 + ## 0.1.35 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index 595c9d355a..be2efd8c93 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-openapi", - "version": "0.1.35", + "version": "0.1.36-next.1", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md index 2396f4b45b..b2604c469e 100644 --- a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md +++ b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-catalog-backend-module-puppetdb +## 0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.1.23 ### Patch Changes diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index 09996cd995..6c38b658e3 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-puppetdb", - "version": "0.1.23", + "version": "0.1.24-next.1", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md index fa0cede13b..b7b46ed403 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md +++ b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-catalog-backend-module-scaffolder-entity-model +## 0.1.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.5.2-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-scaffolder-common@1.5.2-next.0 + ## 0.1.15 ### Patch Changes diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/package.json b/plugins/catalog-backend-module-scaffolder-entity-model/package.json index 994ee2dad9..6b8bc12493 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/package.json +++ b/plugins/catalog-backend-module-scaffolder-entity-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-scaffolder-entity-model", - "version": "0.1.15", + "version": "0.1.16-next.1", "description": "Adds support for the scaffolder specific entity model (e.g. the Template kind) to the catalog backend plugin.", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md index 4e4caa0559..a53e36a696 100644 --- a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md +++ b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-catalog-backend-module-unprocessed +## 0.4.5-next.1 + +### Patch Changes + +- 6f5a3a3: Correctly convert owner to string in case owner has not been provided +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.1 + - @backstage/plugin-permission-common@0.7.13 + ## 0.4.4 ### Patch Changes diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index b1236bf37e..38d96e62e9 100644 --- a/plugins/catalog-backend-module-unprocessed/package.json +++ b/plugins/catalog-backend-module-unprocessed/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-unprocessed", - "version": "0.4.4", + "version": "0.4.5-next.1", "description": "Backstage Catalog module to view unprocessed entities", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts b/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts index 23042ff2d8..34aedd61af 100644 --- a/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts +++ b/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts @@ -162,7 +162,8 @@ export class UnprocessedEntitiesModule { return res.json( await this.unprocessed({ reason: 'failed', - owner: String(req.query.owner), + owner: + typeof req.query.owner === 'string' ? req.query.owner : undefined, }), ); }) @@ -170,7 +171,8 @@ export class UnprocessedEntitiesModule { return res.json( await this.unprocessed({ reason: 'pending', - owner: String(req.query.owner), + owner: + typeof req.query.owner === 'string' ? req.query.owner : undefined, }), ); }) diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index 50920f8db6..426333c567 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,47 @@ # @backstage/plugin-catalog-backend +## 1.22.0-next.1 + +### Patch Changes + +- 8479a0b: Fixed bug in stitching queue gauge that included entities that are scheduled in the future. +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.24-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + - @backstage/backend-openapi-utils@0.1.11-next.1 + +## 1.22.0-next.0 + +### Minor Changes + +- 8d14475: Emit well known relationships for the Domain entity kind. + +### Patch Changes + +- c6cb568: Add lifecycle monitoring for the catalog processing +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-search-backend-module-catalog@0.1.23-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-openapi-utils@0.1.11-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.3.4-next.0 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + ## 1.21.1 ### Patch Changes diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index 27c00257a5..1c65abf83e 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -41,6 +41,7 @@ import { EntityRelationSpec as EntityRelationSpec_2 } from '@backstage/plugin-ca import { EventBroker } from '@backstage/plugin-events-node'; import { GetEntitiesRequest } from '@backstage/catalog-client'; import { HttpAuthService } from '@backstage/backend-plugin-api'; +import { LocationAnalyzer as LocationAnalyzer_2 } from '@backstage/plugin-catalog-node'; import { LocationSpec as LocationSpec_2 } from '@backstage/plugin-catalog-common'; import { locationSpecToLocationEntity as locationSpecToLocationEntity_2 } from '@backstage/plugin-catalog-node'; import { locationSpecToMetadataName as locationSpecToMetadataName_2 } from '@backstage/plugin-catalog-node'; @@ -167,7 +168,7 @@ export class CatalogBuilder { setEntityDataParser(parser: CatalogProcessorParser_2): CatalogBuilder; setEventBroker(broker: EventBroker): CatalogBuilder; setFieldFormatValidators(validators: Partial<Validators>): CatalogBuilder; - setLocationAnalyzer(locationAnalyzer: LocationAnalyzer): CatalogBuilder; + setLocationAnalyzer(locationAnalyzer: LocationAnalyzer_2): CatalogBuilder; setPlaceholderResolver( key: string, resolver: PlaceholderResolver_2, @@ -359,12 +360,8 @@ export class FileReaderProcessor implements CatalogProcessor_2 { ): Promise<boolean>; } -// @public (undocumented) -export type LocationAnalyzer = { - analyzeLocation( - location: AnalyzeLocationRequest_2, - ): Promise<AnalyzeLocationResponse_2>; -}; +// @public @deprecated (undocumented) +export type LocationAnalyzer = LocationAnalyzer_2; // @public @deprecated export class LocationEntityProcessor implements CatalogProcessor_2 { diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 928881f5b4..cea3f06c6f 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "1.21.1", + "version": "1.22.0-next.1", "description": "The Backstage backend plugin that provides the Backstage catalog", "backstage": { "role": "backend-plugin" @@ -43,6 +43,7 @@ "scripts": { "build": "backstage-cli package build", "clean": "backstage-cli package clean", + "diff": "backstage-repo-tools package schema openapi diff", "fuzz": "backstage-repo-tools package schema openapi fuzz --exclude-checks response_schema_conformance", "generate": "backstage-repo-tools package schema openapi generate --server --client-package packages/catalog-client", "lint": "backstage-cli package lint", diff --git a/plugins/catalog-backend/src/deprecated.ts b/plugins/catalog-backend/src/deprecated.ts index 72c3cad225..2d08d2ce0c 100644 --- a/plugins/catalog-backend/src/deprecated.ts +++ b/plugins/catalog-backend/src/deprecated.ts @@ -48,6 +48,7 @@ import { type PlaceholderResolverParams as _PlaceholderResolverParams, type PlaceholderResolverRead as _PlaceholderResolverRead, type PlaceholderResolverResolveUrl as _PlaceholderResolverResolveUrl, + type LocationAnalyzer as _LocationAnalyzer, type ScmLocationAnalyzer as _ScmLocationAnalyzer, } from '@backstage/plugin-catalog-node'; import { @@ -177,6 +178,11 @@ export type LocationSpec = _LocationSpec; * @deprecated import from `@backstage/plugin-catalog-node` instead */ export type AnalyzeOptions = _AnalyzeOptions; +/** + * @public + * @deprecated import from `@backstage/plugin-catalog-node` instead + */ +export type LocationAnalyzer = _LocationAnalyzer; /** * @public * @deprecated import from `@backstage/plugin-catalog-node` instead diff --git a/plugins/catalog-backend/src/index.ts b/plugins/catalog-backend/src/index.ts index 6a479babdc..d3ffa9a974 100644 --- a/plugins/catalog-backend/src/index.ts +++ b/plugins/catalog-backend/src/index.ts @@ -20,7 +20,6 @@ * @packageDocumentation */ -export * from './ingestion'; export * from './modules'; export * from './processing'; export * from './search'; diff --git a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts index 61d319bf37..d574f3b299 100644 --- a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts +++ b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts @@ -17,12 +17,14 @@ import parseGitUrl from 'git-url-parse'; import { Entity } from '@backstage/catalog-model'; import { ScmIntegrationRegistry } from '@backstage/integration'; -import { LocationAnalyzer } from './types'; import { AnalyzeLocationRequest, AnalyzeLocationResponse, } from '@backstage/plugin-catalog-common'; -import { ScmLocationAnalyzer } from '@backstage/plugin-catalog-node'; +import { + LocationAnalyzer, + ScmLocationAnalyzer, +} from '@backstage/plugin-catalog-node'; import { LoggerService } from '@backstage/backend-plugin-api'; export class RepoLocationAnalyzer implements LocationAnalyzer { diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index 2438dc9009..f305bf2d8b 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -45,6 +45,7 @@ import { EntitiesSearchFilter, EntityProvider, PlaceholderResolver, + LocationAnalyzer, ScmLocationAnalyzer, } from '@backstage/plugin-catalog-node'; import { @@ -64,7 +65,6 @@ import { yamlPlaceholderResolver, } from '../modules/core/PlaceholderProcessor'; import { defaultEntityDataParser } from '../modules/util/parse'; -import { LocationAnalyzer } from '../ingestion'; import { CatalogProcessingEngine, createRandomProcessingInterval, diff --git a/plugins/catalog-backend/src/service/CatalogPlugin.ts b/plugins/catalog-backend/src/service/CatalogPlugin.ts index 039d9368bf..ac869cdc2b 100644 --- a/plugins/catalog-backend/src/service/CatalogPlugin.ts +++ b/plugins/catalog-backend/src/service/CatalogPlugin.ts @@ -20,7 +20,6 @@ import { import { Entity, Validators } from '@backstage/catalog-model'; import { CatalogBuilder, CatalogPermissionRuleInput } from './CatalogBuilder'; import { - CatalogAnalysisExtensionPoint, catalogAnalysisExtensionPoint, CatalogModelExtensionPoint, catalogModelExtensionPoint, @@ -33,11 +32,13 @@ import { CatalogProcessor, CatalogProcessorParser, EntityProvider, + LocationAnalyzer, PlaceholderResolver, ScmLocationAnalyzer, } from '@backstage/plugin-catalog-node'; import { merge } from 'lodash'; import { Permission } from '@backstage/plugin-permission-common'; +import { ForwardedError } from '@backstage/errors'; class CatalogProcessingExtensionPointImpl implements CatalogProcessingExtensionPoint @@ -96,20 +97,6 @@ class CatalogProcessingExtensionPointImpl } } -class CatalogAnalysisExtensionPointImpl - implements CatalogAnalysisExtensionPoint -{ - #locationAnalyzers = new Array<ScmLocationAnalyzer>(); - - addLocationAnalyzer(analyzer: ScmLocationAnalyzer): void { - this.#locationAnalyzers.push(analyzer); - } - - get locationAnalyzers() { - return this.#locationAnalyzers; - } -} - class CatalogPermissionExtensionPointImpl implements CatalogPermissionExtensionPoint { @@ -178,11 +165,29 @@ export const catalogPlugin = createBackendPlugin({ processingExtensions, ); - const analysisExtensions = new CatalogAnalysisExtensionPointImpl(); - env.registerExtensionPoint( - catalogAnalysisExtensionPoint, - analysisExtensions, - ); + let locationAnalyzerFactory: + | ((options: { + scmLocationAnalyzers: ScmLocationAnalyzer[]; + }) => Promise<{ locationAnalyzer: LocationAnalyzer }>) + | undefined = undefined; + const scmLocationAnalyzers = new Array<ScmLocationAnalyzer>(); + env.registerExtensionPoint(catalogAnalysisExtensionPoint, { + setLocationAnalyzer(analyzerOrFactory) { + if (locationAnalyzerFactory) { + throw new Error('LocationAnalyzer has already been set'); + } + if (typeof analyzerOrFactory === 'function') { + locationAnalyzerFactory = analyzerOrFactory; + } else { + locationAnalyzerFactory = async () => ({ + locationAnalyzer: analyzerOrFactory, + }); + } + }, + addScmLocationAnalyzer(analyzer: ScmLocationAnalyzer) { + scmLocationAnalyzers.push(analyzer); + }, + }); const permissionExtensions = new CatalogPermissionExtensionPointImpl(); env.registerExtensionPoint( @@ -201,7 +206,7 @@ export const catalogPlugin = createBackendPlugin({ permissions: coreServices.permissions, database: coreServices.database, httpRouter: coreServices.httpRouter, - lifecycle: coreServices.lifecycle, + lifecycle: coreServices.rootLifecycle, scheduler: coreServices.scheduler, discovery: coreServices.discovery, auth: coreServices.auth, @@ -246,14 +251,25 @@ export const catalogPlugin = createBackendPlugin({ Object.entries(processingExtensions.placeholderResolvers).forEach( ([key, resolver]) => builder.setPlaceholderResolver(key, resolver), ); - builder.addLocationAnalyzers(...analysisExtensions.locationAnalyzers); + if (locationAnalyzerFactory) { + const { locationAnalyzer } = await locationAnalyzerFactory({ + scmLocationAnalyzers, + }).catch(e => { + throw new ForwardedError('Failed to create LocationAnalyzer', e); + }); + builder.setLocationAnalyzer(locationAnalyzer); + } else { + builder.addLocationAnalyzers(...scmLocationAnalyzers); + } builder.addPermissions(...permissionExtensions.permissions); builder.addPermissionRules(...permissionExtensions.permissionRules); builder.setFieldFormatValidators(modelExtensions.fieldValidators); const { processingEngine, router } = await builder.build(); - await processingEngine.start(); + lifecycle.addStartupHook(async () => { + await processingEngine.start(); + }); lifecycle.addShutdownHook(() => processingEngine.stop()); httpRouter.use(router); }, diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index 292c4c1b7a..a2fae0e8bd 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -42,7 +42,7 @@ import { decodeCursor, encodeCursor } from './util'; import { wrapInOpenApiTestServer } from '@backstage/backend-openapi-utils'; import { Server } from 'http'; import { mockCredentials, mockServices } from '@backstage/backend-test-utils'; -import { LocationAnalyzer } from '../ingestion'; +import { LocationAnalyzer } from '@backstage/plugin-catalog-node'; describe('createRouter readonly disabled', () => { let entitiesCatalog: jest.Mocked<EntitiesCatalog>; diff --git a/plugins/catalog-backend/src/service/createRouter.ts b/plugins/catalog-backend/src/service/createRouter.ts index 508f0bb3d6..774ee61a6d 100644 --- a/plugins/catalog-backend/src/service/createRouter.ts +++ b/plugins/catalog-backend/src/service/createRouter.ts @@ -28,7 +28,6 @@ import express from 'express'; import yn from 'yn'; import { z } from 'zod'; import { EntitiesCatalog } from '../catalog/types'; -import { LocationAnalyzer } from '../ingestion'; import { CatalogProcessingOrchestrator } from '../processing/types'; import { validateEntityEnvelope } from '../processing/util'; import { @@ -55,6 +54,7 @@ import { HttpAuthService, LoggerService, } from '@backstage/backend-plugin-api'; +import { LocationAnalyzer } from '@backstage/plugin-catalog-node'; /** * Options used by {@link createRouter}. diff --git a/plugins/catalog-backend/src/stitching/progressTracker.ts b/plugins/catalog-backend/src/stitching/progressTracker.ts index 9e00a1a01b..8c305b4978 100644 --- a/plugins/catalog-backend/src/stitching/progressTracker.ts +++ b/plugins/catalog-backend/src/stitching/progressTracker.ts @@ -54,7 +54,8 @@ export function progressTracker(knex: Knex, logger: LoggerService) { stitchingQueueCount.addCallback(async result => { const total = await knex<DbRefreshStateRow>('refresh_state') .count({ count: '*' }) - .whereNotNull('next_stitch_at'); + .whereNotNull('next_stitch_at') + .where('next_stitch_at', '<=', knex.fn.now()); result.observe(Number(total[0].count)); }); diff --git a/plugins/catalog-common/CHANGELOG.md b/plugins/catalog-common/CHANGELOG.md index 41213ed60c..f070ca11e2 100644 --- a/plugins/catalog-common/CHANGELOG.md +++ b/plugins/catalog-common/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-common +## 1.0.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-search-common@1.2.11 + ## 1.0.22 ### Patch Changes diff --git a/plugins/catalog-common/package.json b/plugins/catalog-common/package.json index 64544ade1c..59d0019512 100644 --- a/plugins/catalog-common/package.json +++ b/plugins/catalog-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-common", - "version": "1.0.22", + "version": "1.0.23-next.0", "description": "Common functionalities for the catalog plugin", "backstage": { "role": "common-library" diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index cabd0b89fd..e1b79d8d13 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-catalog-graph +## 0.4.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## 0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/types@1.1.1 + ## 0.4.4 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 45c519eff4..d0363f5376 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.4.4", + "version": "0.4.5-next.1", "backstage": { "role": "frontend-plugin" }, diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index 70e199ff34..bfdfabba5a 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,34 @@ # @backstage/plugin-catalog-import +## 0.10.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/core-compat-api@0.2.5-next.1 + +## 0.10.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/integration@1.10.0 + - @backstage/integration-react@1.1.26 + ## 0.10.10 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 67215f6a74..a766110a4d 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.10.10", + "version": "0.10.11-next.1", "description": "A Backstage plugin the helps you import entities into your catalog", "backstage": { "role": "frontend-plugin" diff --git a/plugins/catalog-node/CHANGELOG.md b/plugins/catalog-node/CHANGELOG.md index 9f1f9a525a..a2405a7eef 100644 --- a/plugins/catalog-node/CHANGELOG.md +++ b/plugins/catalog-node/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-catalog-node +## 1.11.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 1.11.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + ## 1.11.1 ### Patch Changes diff --git a/plugins/catalog-node/api-report-alpha.md b/plugins/catalog-node/api-report-alpha.md index fb41d6f03e..b61d1b1d27 100644 --- a/plugins/catalog-node/api-report-alpha.md +++ b/plugins/catalog-node/api-report-alpha.md @@ -10,6 +10,7 @@ import { EntitiesSearchFilter } from '@backstage/plugin-catalog-node'; import { Entity } from '@backstage/catalog-model'; import { EntityProvider } from '@backstage/plugin-catalog-node'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; +import { LocationAnalyzer } from '@backstage/plugin-catalog-node'; import { Permission } from '@backstage/plugin-permission-common'; import { PermissionRule } from '@backstage/plugin-permission-node'; import { PermissionRuleParams } from '@backstage/plugin-permission-common'; @@ -20,8 +21,14 @@ import { Validators } from '@backstage/catalog-model'; // @alpha (undocumented) export interface CatalogAnalysisExtensionPoint { - // (undocumented) - addLocationAnalyzer(analyzer: ScmLocationAnalyzer): void; + addScmLocationAnalyzer(analyzer: ScmLocationAnalyzer): void; + setLocationAnalyzer( + analyzerOrFactory: + | LocationAnalyzer + | ((options: { scmLocationAnalyzers: ScmLocationAnalyzer[] }) => Promise<{ + locationAnalyzer: LocationAnalyzer; + }>), + ): void; } // @alpha (undocumented) diff --git a/plugins/catalog-node/api-report.md b/plugins/catalog-node/api-report.md index a4ba95073f..7c1c8b5911 100644 --- a/plugins/catalog-node/api-report.md +++ b/plugins/catalog-node/api-report.md @@ -6,6 +6,8 @@ /// <reference types="node" /> import { AnalyzeLocationExistingEntity } from '@backstage/plugin-catalog-common'; +import { AnalyzeLocationRequest } from '@backstage/plugin-catalog-common'; +import { AnalyzeLocationResponse } from '@backstage/plugin-catalog-common'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { Entity } from '@backstage/catalog-model'; import { JsonValue } from '@backstage/types'; @@ -166,6 +168,13 @@ export type EntityRelationSpec = { target: CompoundEntityRef; }; +// @public (undocumented) +export type LocationAnalyzer = { + analyzeLocation( + location: AnalyzeLocationRequest, + ): Promise<AnalyzeLocationResponse>; +}; + // @public @deprecated export type LocationSpec = LocationSpec_2; diff --git a/plugins/catalog-node/package.json b/plugins/catalog-node/package.json index 37cc1accfb..ebfea63cef 100644 --- a/plugins/catalog-node/package.json +++ b/plugins/catalog-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-node", - "version": "1.11.1", + "version": "1.11.2-next.1", "description": "The plugin-catalog-node module for @backstage/plugin-catalog-backend", "backstage": { "role": "node-library" diff --git a/plugins/catalog-node/src/extensions.ts b/plugins/catalog-node/src/extensions.ts index 648b8c5974..7c1fe6c30d 100644 --- a/plugins/catalog-node/src/extensions.ts +++ b/plugins/catalog-node/src/extensions.ts @@ -22,6 +22,7 @@ import { EntitiesSearchFilter, EntityProvider, PlaceholderResolver, + LocationAnalyzer, ScmLocationAnalyzer, } from '@backstage/plugin-catalog-node'; import { @@ -79,7 +80,27 @@ export const catalogProcessingExtensionPoint = * @alpha */ export interface CatalogAnalysisExtensionPoint { - addLocationAnalyzer(analyzer: ScmLocationAnalyzer): void; + /** + * Replaces the entire location analyzer with a new one. + * + * @remarks + * + * By providing a factory function you can access all the SCM analyzers that + * have been added through `addScmLocationAnalyzer`. If you provide a + * `LocationAnalyzer` directly, the SCM analyzers will be ignored. + */ + setLocationAnalyzer( + analyzerOrFactory: + | LocationAnalyzer + | ((options: { + scmLocationAnalyzers: ScmLocationAnalyzer[]; + }) => Promise<{ locationAnalyzer: LocationAnalyzer }>), + ): void; + + /** + * Adds an analyzer for a specific SCM type to the default location analyzer. + */ + addScmLocationAnalyzer(analyzer: ScmLocationAnalyzer): void; } /** diff --git a/plugins/catalog-node/src/processing/index.ts b/plugins/catalog-node/src/processing/index.ts index 8cffac6961..4e83e6077d 100644 --- a/plugins/catalog-node/src/processing/index.ts +++ b/plugins/catalog-node/src/processing/index.ts @@ -21,5 +21,6 @@ export type { PlaceholderResolverParams, PlaceholderResolverRead, PlaceholderResolverResolveUrl, + LocationAnalyzer, ScmLocationAnalyzer, } from './types'; diff --git a/plugins/catalog-node/src/processing/types.ts b/plugins/catalog-node/src/processing/types.ts index b6785c1b91..e3d3c305fc 100644 --- a/plugins/catalog-node/src/processing/types.ts +++ b/plugins/catalog-node/src/processing/types.ts @@ -15,7 +15,11 @@ */ import { Entity } from '@backstage/catalog-model'; -import { AnalyzeLocationExistingEntity } from '@backstage/plugin-catalog-common'; +import { + AnalyzeLocationExistingEntity, + AnalyzeLocationRequest, + AnalyzeLocationResponse, +} from '@backstage/plugin-catalog-common'; import { JsonValue } from '@backstage/types'; import { CatalogProcessorEmit } from '../api'; @@ -52,6 +56,19 @@ export type PlaceholderResolver = ( params: PlaceholderResolverParams, ) => Promise<JsonValue>; +/** @public */ +export type LocationAnalyzer = { + /** + * Generates an entity configuration for given git repository. It's used for + * importing new component to the backstage app. + * + * @param location - Git repository to analyze and generate config for. + */ + analyzeLocation( + location: AnalyzeLocationRequest, + ): Promise<AnalyzeLocationResponse>; +}; + /** @public */ export type AnalyzeOptions = { url: string; diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index 52892384ca..190773044a 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-catalog-react +## 1.11.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + +## 1.11.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/integration-react@1.1.26 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-react@0.4.22 + ## 1.11.3 ### Patch Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index fc7654e3f2..041253f223 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-react", "description": "A frontend library that helps other Backstage plugins interact with the catalog", - "version": "1.11.3", + "version": "1.11.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-unprocessed-entities/CHANGELOG.md b/plugins/catalog-unprocessed-entities/CHANGELOG.md index b6e807222f..06d4344889 100644 --- a/plugins/catalog-unprocessed-entities/CHANGELOG.md +++ b/plugins/catalog-unprocessed-entities/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-unprocessed-entities +## 0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## 0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + ## 0.2.3 ### Patch Changes diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index af4eb53a43..eb35eae31e 100644 --- a/plugins/catalog-unprocessed-entities/package.json +++ b/plugins/catalog-unprocessed-entities/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-unprocessed-entities", - "version": "0.2.3", + "version": "0.2.4-next.1", "backstage": { "role": "frontend-plugin" }, diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 0512679fa1..300083da1c 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,40 @@ # @backstage/plugin-catalog +## 1.19.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.5.2-next.1 + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-search-react@1.7.11-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## 1.19.1-next.0 + +### Patch Changes + +- 5d99272: Update local development dependencies. +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-scaffolder-common@1.5.2-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/integration-react@1.1.26 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-react@0.4.22 + - @backstage/plugin-search-common@1.2.11 + - @backstage/plugin-search-react@1.7.11-next.0 + ## 1.19.0 ### Minor Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 8ddfda02d5..207717e1c9 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "1.19.0", + "version": "1.19.1-next.1", "description": "The Backstage plugin for browsing the Backstage catalog", "backstage": { "role": "frontend-plugin" diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx index 052d8f4787..35639069df 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx @@ -343,7 +343,7 @@ describe('CatalogTable component', () => { expect(screen.getByText('Should be rendered')).toBeInTheDocument(); }); - it('should render the label column with customised title and value as specified', async () => { + it('should render the label column with customized title and value as specified', async () => { const columns = [ CatalogTable.columns.createNameColumn({ defaultKind: 'API' }), CatalogTable.columns.createLabelColumn('category', { title: 'Category' }), @@ -381,7 +381,7 @@ describe('CatalogTable component', () => { expect(labelCellValue).toBeInTheDocument(); }); - it('should render the label column with customised title and value as specified using function', async () => { + it('should render the label column with customized title and value as specified using function', async () => { const columns: CatalogTableColumnsFunc = ({ filters, entities: entities1, diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 674271a044..b1590bdff3 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -91,7 +91,6 @@ export const CatalogTable = (props: CatalogTableProps) => { const { loading, error, entities, filters, pageInfo, totalItems } = entityListContext; const enablePagination = !!pageInfo; - const tableColumns = useMemo( () => typeof columns === 'function' ? columns(entityListContext) : columns, @@ -170,13 +169,18 @@ export const CatalogTable = (props: CatalogTableProps) => { const currentKind = filters.kind?.value || ''; const currentType = filters.type?.value || ''; + const currentCount = typeof totalItems === 'number' ? `(${totalItems})` : ''; // TODO(timbonicus): remove the title from the CatalogTable once using EntitySearchBar const titlePreamble = capitalize(filters.user?.value ?? 'all'); - const titleDisplay = [titlePreamble, currentType, pluralize(currentKind)] + const title = [ + titlePreamble, + currentType, + pluralize(currentKind), + currentCount, + ] .filter(s => s) .join(' '); - const title = `${titleDisplay} (${totalItems})`; const actions = props.actions || defaultActions; const options = { actionsColumnIndex: -1, @@ -192,7 +196,7 @@ export const CatalogTable = (props: CatalogTableProps) => { columns={tableColumns} emptyContent={emptyContent} isLoading={loading} - title={titleDisplay} + title={title} actions={actions} subtitle={subtitle} options={options} diff --git a/plugins/config-schema/CHANGELOG.md b/plugins/config-schema/CHANGELOG.md index 878d451197..792691f33d 100644 --- a/plugins/config-schema/CHANGELOG.md +++ b/plugins/config-schema/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-config-schema +## 0.1.55-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## 0.1.55-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.1.54 ### Patch Changes diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index f207c465f0..19f2401602 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-config-schema", - "version": "0.1.54", + "version": "0.1.55-next.1", "description": "A Backstage plugin that lets you browse the configuration schema of your app", "backstage": { "role": "frontend-plugin" diff --git a/plugins/devtools-backend/CHANGELOG.md b/plugins/devtools-backend/CHANGELOG.md index 8401cb0555..1d4247cb9f 100644 --- a/plugins/devtools-backend/CHANGELOG.md +++ b/plugins/devtools-backend/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-devtools-backend +## 0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/config-loader@1.8.0 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.8.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-devtools-common@0.1.9 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + ## 0.3.3 ### Patch Changes diff --git a/plugins/devtools-backend/README.md b/plugins/devtools-backend/README.md index 4439c8cd9f..bf0eb64a98 100644 --- a/plugins/devtools-backend/README.md +++ b/plugins/devtools-backend/README.md @@ -28,6 +28,7 @@ Here's how to get the DevTools Backend up and running: logger: env.logger, config: env.config, permissions: env.permissions, + discovery: env.discovery, }); } ``` diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index f1d48759ef..e9c6f48b49 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-backend", - "version": "0.3.3", + "version": "0.3.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/devtools/CHANGELOG.md b/plugins/devtools/CHANGELOG.md index 9ae62bd3bd..b618417ce0 100644 --- a/plugins/devtools/CHANGELOG.md +++ b/plugins/devtools/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-devtools +## 0.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## 0.1.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/plugin-devtools-common@0.1.9 + - @backstage/plugin-permission-react@0.4.22 + ## 0.1.13 ### Patch Changes diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index 306a5f4180..64b3626546 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools", - "version": "0.1.13", + "version": "0.1.14-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-aws-sqs/CHANGELOG.md b/plugins/events-backend-module-aws-sqs/CHANGELOG.md index 6897609c22..498f89b6fa 100644 --- a/plugins/events-backend-module-aws-sqs/CHANGELOG.md +++ b/plugins/events-backend-module-aws-sqs/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-events-backend-module-aws-sqs +## 0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.3.4-next.0 + ## 0.3.3 ### Patch Changes diff --git a/plugins/events-backend-module-aws-sqs/package.json b/plugins/events-backend-module-aws-sqs/package.json index e5bcf184b1..ab33e2c451 100644 --- a/plugins/events-backend-module-aws-sqs/package.json +++ b/plugins/events-backend-module-aws-sqs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-aws-sqs", - "version": "0.3.3", + "version": "0.3.4-next.1", "backstage": { "role": "backend-plugin-module" }, diff --git a/plugins/events-backend-module-azure/CHANGELOG.md b/plugins/events-backend-module-azure/CHANGELOG.md index feb1ea429f..1fbb360beb 100644 --- a/plugins/events-backend-module-azure/CHANGELOG.md +++ b/plugins/events-backend-module-azure/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-events-backend-module-azure +## 0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-events-node@0.3.4-next.0 + ## 0.2.3 ### Patch Changes diff --git a/plugins/events-backend-module-azure/package.json b/plugins/events-backend-module-azure/package.json index ed45ca3923..f7ade8b3a2 100644 --- a/plugins/events-backend-module-azure/package.json +++ b/plugins/events-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-azure", - "version": "0.2.3", + "version": "0.2.4-next.1", "backstage": { "role": "backend-plugin-module" }, diff --git a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md index 820e31aad5..b649c38b5b 100644 --- a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-events-backend-module-bitbucket-cloud +## 0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-events-node@0.3.4-next.0 + ## 0.2.3 ### Patch Changes diff --git a/plugins/events-backend-module-bitbucket-cloud/package.json b/plugins/events-backend-module-bitbucket-cloud/package.json index 0085f2de2a..baed6171fd 100644 --- a/plugins/events-backend-module-bitbucket-cloud/package.json +++ b/plugins/events-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-bitbucket-cloud", - "version": "0.2.3", + "version": "0.2.4-next.1", "backstage": { "role": "backend-plugin-module" }, diff --git a/plugins/events-backend-module-gerrit/CHANGELOG.md b/plugins/events-backend-module-gerrit/CHANGELOG.md index 20ed29a1a3..57c6390296 100644 --- a/plugins/events-backend-module-gerrit/CHANGELOG.md +++ b/plugins/events-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-events-backend-module-gerrit +## 0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-events-node@0.3.4-next.0 + ## 0.2.3 ### Patch Changes diff --git a/plugins/events-backend-module-gerrit/package.json b/plugins/events-backend-module-gerrit/package.json index 421cdbf5e6..b3f8161604 100644 --- a/plugins/events-backend-module-gerrit/package.json +++ b/plugins/events-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-gerrit", - "version": "0.2.3", + "version": "0.2.4-next.1", "backstage": { "role": "backend-plugin-module" }, diff --git a/plugins/events-backend-module-github/CHANGELOG.md b/plugins/events-backend-module-github/CHANGELOG.md index 886e6e7103..ae5ee59263 100644 --- a/plugins/events-backend-module-github/CHANGELOG.md +++ b/plugins/events-backend-module-github/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-events-backend-module-github +## 0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-events-node@0.3.4-next.0 + ## 0.2.3 ### Patch Changes diff --git a/plugins/events-backend-module-github/package.json b/plugins/events-backend-module-github/package.json index 4cc5d0c456..9fb876f714 100644 --- a/plugins/events-backend-module-github/package.json +++ b/plugins/events-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-github", - "version": "0.2.3", + "version": "0.2.4-next.1", "backstage": { "role": "backend-plugin-module" }, diff --git a/plugins/events-backend-module-gitlab/CHANGELOG.md b/plugins/events-backend-module-gitlab/CHANGELOG.md index 0f7d9d1b0a..73c4c7e281 100644 --- a/plugins/events-backend-module-gitlab/CHANGELOG.md +++ b/plugins/events-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-events-backend-module-gitlab +## 0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-events-node@0.3.4-next.0 + ## 0.2.3 ### Patch Changes diff --git a/plugins/events-backend-module-gitlab/package.json b/plugins/events-backend-module-gitlab/package.json index ebc9d5f423..e10b26736f 100644 --- a/plugins/events-backend-module-gitlab/package.json +++ b/plugins/events-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-gitlab", - "version": "0.2.3", + "version": "0.2.4-next.1", "backstage": { "role": "backend-plugin-module" }, diff --git a/plugins/events-backend-test-utils/CHANGELOG.md b/plugins/events-backend-test-utils/CHANGELOG.md index 2af39cc211..f5ba8b9609 100644 --- a/plugins/events-backend-test-utils/CHANGELOG.md +++ b/plugins/events-backend-test-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-events-backend-test-utils +## 0.1.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.3.4-next.1 + +## 0.1.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.3.4-next.0 + ## 0.1.27 ### Patch Changes diff --git a/plugins/events-backend-test-utils/package.json b/plugins/events-backend-test-utils/package.json index 9108c4eda2..f766130efc 100644 --- a/plugins/events-backend-test-utils/package.json +++ b/plugins/events-backend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-test-utils", - "version": "0.1.27", + "version": "0.1.28-next.1", "description": "The plugin-events-backend-test-utils for @backstage/plugin-events-node", "backstage": { "role": "node-library" diff --git a/plugins/events-backend/CHANGELOG.md b/plugins/events-backend/CHANGELOG.md index ec49ebfd9c..1bb33b6db1 100644 --- a/plugins/events-backend/CHANGELOG.md +++ b/plugins/events-backend/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-events-backend +## 0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-events-node@0.3.4-next.0 + ## 0.3.4 ### Patch Changes diff --git a/plugins/events-backend/package.json b/plugins/events-backend/package.json index c618b164b0..dc36a33d07 100644 --- a/plugins/events-backend/package.json +++ b/plugins/events-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend", - "version": "0.3.4", + "version": "0.3.5-next.1", "backstage": { "role": "backend-plugin" }, diff --git a/plugins/events-node/CHANGELOG.md b/plugins/events-node/CHANGELOG.md index 40b8350304..185da613eb 100644 --- a/plugins/events-node/CHANGELOG.md +++ b/plugins/events-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-events-node +## 0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + ## 0.3.3 ### Patch Changes diff --git a/plugins/events-node/package.json b/plugins/events-node/package.json index 77ab5e015d..7312950a99 100644 --- a/plugins/events-node/package.json +++ b/plugins/events-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-node", - "version": "0.3.3", + "version": "0.3.4-next.1", "description": "The plugin-events-node module for @backstage/plugin-events-backend", "backstage": { "role": "node-library" diff --git a/plugins/example-todo-list-backend/CHANGELOG.md b/plugins/example-todo-list-backend/CHANGELOG.md index c71f8a24b4..2d6dee39e2 100644 --- a/plugins/example-todo-list-backend/CHANGELOG.md +++ b/plugins/example-todo-list-backend/CHANGELOG.md @@ -1,5 +1,24 @@ # @internal/plugin-todo-list-backend +## 1.0.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 1.0.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/errors@1.2.4 + ## 1.0.26 ### Patch Changes diff --git a/plugins/example-todo-list-backend/package.json b/plugins/example-todo-list-backend/package.json index 0c2c12bf04..cd45af6d37 100644 --- a/plugins/example-todo-list-backend/package.json +++ b/plugins/example-todo-list-backend/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list-backend", - "version": "1.0.26", + "version": "1.0.27-next.1", "backstage": { "role": "backend-plugin" }, @@ -20,8 +20,7 @@ "main": "src/index.ts", "types": "src/index.ts", "files": [ - "dist", - "alpha" + "dist" ], "scripts": { "build": "backstage-cli package build", diff --git a/plugins/example-todo-list/CHANGELOG.md b/plugins/example-todo-list/CHANGELOG.md index 092e5857e4..0962426ac3 100644 --- a/plugins/example-todo-list/CHANGELOG.md +++ b/plugins/example-todo-list/CHANGELOG.md @@ -1,5 +1,20 @@ # @internal/plugin-todo-list +## 1.0.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## 1.0.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + ## 1.0.26 ### Patch Changes diff --git a/plugins/example-todo-list/package.json b/plugins/example-todo-list/package.json index ff192a13b5..849b96433a 100644 --- a/plugins/example-todo-list/package.json +++ b/plugins/example-todo-list/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list", - "version": "1.0.26", + "version": "1.0.27-next.1", "backstage": { "role": "frontend-plugin" }, diff --git a/plugins/home-react/CHANGELOG.md b/plugins/home-react/CHANGELOG.md index fee07a2458..b1f8f6793e 100644 --- a/plugins/home-react/CHANGELOG.md +++ b/plugins/home-react/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-home-react +## 0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## 0.1.13-next.0 + +### Patch Changes + +- 0040ec2: Updated dependency `@rjsf/utils` to `5.18.2`. + Updated dependency `@rjsf/core` to `5.18.2`. + Updated dependency `@rjsf/material-ui` to `5.18.2`. + Updated dependency `@rjsf/validator-ajv8` to `5.18.2`. +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + ## 0.1.12 ### Patch Changes diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index a3e9d0f267..bb04ce8211 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home-react", - "version": "0.1.12", + "version": "0.1.13-next.1", "description": "A Backstage plugin that contains react components helps you build a home page", "backstage": { "role": "web-library" diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index d7a72678c4..565d82f395 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-home +## 0.7.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/plugin-home-react@0.1.13-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## 0.7.4-next.0 + +### Patch Changes + +- 2196d3e: Use relative time when displaying visits from the same day +- 0040ec2: Updated dependency `@rjsf/utils` to `5.18.2`. + Updated dependency `@rjsf/core` to `5.18.2`. + Updated dependency `@rjsf/material-ui` to `5.18.2`. + Updated dependency `@rjsf/validator-ajv8` to `5.18.2`. +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-home-react@0.1.13-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.5-next.0 + ## 0.7.3 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index 8c5db6de8e..0058c35e00 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home", - "version": "0.7.3", + "version": "0.7.4-next.1", "description": "A Backstage plugin that helps you build a home page", "backstage": { "role": "frontend-plugin" diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index 4583cf68e2..a1000aea2c 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-kubernetes-backend +## 0.17.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-kubernetes-node@0.1.12-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.17.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-kubernetes-node@0.1.12-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-kubernetes-common@0.7.6-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + ## 0.17.0 ### Minor Changes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 3b2560106a..a8634d8855 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-backend", "description": "A Backstage backend plugin that integrates towards Kubernetes", - "version": "0.17.0", + "version": "0.17.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-cluster/CHANGELOG.md b/plugins/kubernetes-cluster/CHANGELOG.md index 7bcffe9b9a..8ebfd45d77 100644 --- a/plugins/kubernetes-cluster/CHANGELOG.md +++ b/plugins/kubernetes-cluster/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-kubernetes-cluster +## 0.0.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-kubernetes-react@0.3.5-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + +## 0.0.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-kubernetes-common@0.7.6-next.0 + - @backstage/plugin-kubernetes-react@0.3.5-next.0 + - @backstage/core-plugin-api@1.9.2 + ## 0.0.10 ### Patch Changes diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index 1239156ffd..1402c79951 100644 --- a/plugins/kubernetes-cluster/package.json +++ b/plugins/kubernetes-cluster/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-cluster", - "version": "0.0.10", + "version": "0.0.11-next.1", "description": "A Backstage plugin that shows details of Kubernetes clusters", "backstage": { "role": "frontend-plugin" diff --git a/plugins/kubernetes-common/CHANGELOG.md b/plugins/kubernetes-common/CHANGELOG.md index bb1b8664ee..55f3485ff4 100644 --- a/plugins/kubernetes-common/CHANGELOG.md +++ b/plugins/kubernetes-common/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-kubernetes-common +## 0.7.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + ## 0.7.5 ### Patch Changes diff --git a/plugins/kubernetes-common/package.json b/plugins/kubernetes-common/package.json index 2dad38c002..77201e4fbf 100644 --- a/plugins/kubernetes-common/package.json +++ b/plugins/kubernetes-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-common", - "version": "0.7.5", + "version": "0.7.6-next.0", "description": "Common functionalities for kubernetes, to be shared between kubernetes and kubernetes-backend plugin", "backstage": { "role": "common-library" diff --git a/plugins/kubernetes-node/CHANGELOG.md b/plugins/kubernetes-node/CHANGELOG.md index d58ea12ce9..89c7a58526 100644 --- a/plugins/kubernetes-node/CHANGELOG.md +++ b/plugins/kubernetes-node/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-kubernetes-node +## 0.1.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-kubernetes-common@0.7.6-next.0 + - @backstage/types@1.1.1 + ## 0.1.11 ### Patch Changes diff --git a/plugins/kubernetes-node/package.json b/plugins/kubernetes-node/package.json index 4d0e2d9d75..79e304671a 100644 --- a/plugins/kubernetes-node/package.json +++ b/plugins/kubernetes-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-node", - "version": "0.1.11", + "version": "0.1.12-next.1", "description": "Node.js library for the kubernetes plugin", "backstage": { "role": "node-library" diff --git a/plugins/kubernetes-react/CHANGELOG.md b/plugins/kubernetes-react/CHANGELOG.md index 8243bab8ae..e05d103ad6 100644 --- a/plugins/kubernetes-react/CHANGELOG.md +++ b/plugins/kubernetes-react/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-kubernetes-react +## 0.3.5-next.1 + +### Patch Changes + +- 3102a99: add the namespace label to CronJobDrawer & IngressDrawer. +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## 0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-kubernetes-common@0.7.6-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.3.4 ### Patch Changes diff --git a/plugins/kubernetes-react/package.json b/plugins/kubernetes-react/package.json index bc72ac27e9..7ff42ab26c 100644 --- a/plugins/kubernetes-react/package.json +++ b/plugins/kubernetes-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-react", "description": "Web library for the kubernetes-react plugin", - "version": "0.3.4", + "version": "0.3.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-react/src/components/IngressesAccordions/IngressDrawer.tsx b/plugins/kubernetes-react/src/components/IngressesAccordions/IngressDrawer.tsx index 0590546bbd..feb85c6d3d 100644 --- a/plugins/kubernetes-react/src/components/IngressesAccordions/IngressDrawer.tsx +++ b/plugins/kubernetes-react/src/components/IngressesAccordions/IngressDrawer.tsx @@ -19,6 +19,7 @@ import { V1Ingress } from '@kubernetes/client-node'; import { KubernetesStructuredMetadataTableDrawer } from '../KubernetesDrawer'; import Typography from '@material-ui/core/Typography'; import Grid from '@material-ui/core/Grid'; +import Chip from '@material-ui/core/Chip'; export const IngressDrawer = ({ ingress, @@ -27,6 +28,7 @@ export const IngressDrawer = ({ ingress: V1Ingress; expanded?: boolean; }) => { + const namespace = ingress.metadata?.namespace; return ( <KubernetesStructuredMetadataTableDrawer object={ingress} @@ -53,6 +55,11 @@ export const IngressDrawer = ({ Ingress </Typography> </Grid> + {namespace && ( + <Grid item> + <Chip size="small" label={`namespace: ${namespace}`} /> + </Grid> + )} </Grid> </KubernetesStructuredMetadataTableDrawer> ); diff --git a/plugins/kubernetes-react/src/components/ServicesAccordions/ServiceDrawer.tsx b/plugins/kubernetes-react/src/components/ServicesAccordions/ServiceDrawer.tsx index c5392a3e3c..08678e45bd 100644 --- a/plugins/kubernetes-react/src/components/ServicesAccordions/ServiceDrawer.tsx +++ b/plugins/kubernetes-react/src/components/ServicesAccordions/ServiceDrawer.tsx @@ -19,6 +19,7 @@ import { V1Service } from '@kubernetes/client-node'; import { KubernetesStructuredMetadataTableDrawer } from '../KubernetesDrawer'; import Typography from '@material-ui/core/Typography'; import Grid from '@material-ui/core/Grid'; +import Chip from '@material-ui/core/Chip'; export const ServiceDrawer = ({ service, @@ -27,6 +28,7 @@ export const ServiceDrawer = ({ service: V1Service; expanded?: boolean; }) => { + const namespace = service.metadata?.namespace; return ( <KubernetesStructuredMetadataTableDrawer object={service} @@ -53,6 +55,11 @@ export const ServiceDrawer = ({ Service </Typography> </Grid> + {namespace && ( + <Grid item> + <Chip size="small" label={`namespace: ${namespace}`} /> + </Grid> + )} </Grid> </KubernetesStructuredMetadataTableDrawer> ); diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index a01fc187db..de0f70736a 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-kubernetes +## 0.11.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-kubernetes-react@0.3.5-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + +## 0.11.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-kubernetes-common@0.7.6-next.0 + - @backstage/plugin-kubernetes-react@0.3.5-next.0 + - @backstage/core-plugin-api@1.9.2 + ## 0.11.9 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 94d21ff1d1..e75d503558 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes", "description": "A Backstage plugin that integrates towards Kubernetes", - "version": "0.11.9", + "version": "0.11.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/notifications-backend-module-email/.eslintrc.js b/plugins/notifications-backend-module-email/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/notifications-backend-module-email/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/notifications-backend-module-email/CHANGELOG.md b/plugins/notifications-backend-module-email/CHANGELOG.md new file mode 100644 index 0000000000..065f8cb533 --- /dev/null +++ b/plugins/notifications-backend-module-email/CHANGELOG.md @@ -0,0 +1,11 @@ +# @backstage/plugin-notifications-backend-module-email + +## 0.0.1-next.0 + +### Patch Changes + +- dbf2696: Allow sending notifications by email with the new notifications module +- Updated dependencies + - @backstage/plugin-notifications-node@0.1.4-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 diff --git a/plugins/notifications-backend-module-email/README.md b/plugins/notifications-backend-module-email/README.md new file mode 100644 index 0000000000..7bbac05b62 --- /dev/null +++ b/plugins/notifications-backend-module-email/README.md @@ -0,0 +1,72 @@ +# @backstage/plugin-notifications-backend-module-email + +Adds support for sending Backstage notifications as emails to users. + +Supports sending emails using `SMTP`, `SES`, or `sendmail`. + +## Customizing email content + +The email content can be customized with the `notificationsEmailTemplateExtensionPoint`. When you create +this extension, you can set the custom `NotificationTemplateRenderer` to the module. To modify the contents, +override the `getSubject`, `getHtml` and `getText` methods. + +```ts +import { notificationsEmailTemplateExtensionPoint } from '@backstage/plugin-notifications-backend-module-email'; +import { Notification } from '@backstage/plugin-notifications-common'; + +export const notificationsModuleEmailDecorator = createBackendModule({ + pluginId: 'notifications', + moduleId: 'email.templates', + register(reg) { + reg.registerInit({ + deps: { + emailTemplates: notificationsEmailTemplateExtensionPoint, + }, + async init({ emailTemplates }) { + emailTemplates.setTemplateRenderer({ + getSubject(notification) { + return `New notification from ${notification.source}`; + }, + getText(notification) { + return notification.content; + }, + getHtml(notification) { + return `<p>${notification.content}</p>`; + }, + }); + }, + }); + }, +}); +``` + +## Example configuration: + +```yaml +notifications: + processors: + email: + # Transport config, see options at `config.d.ts` + transportConfig: + transport: 'smtp' + hostname: 'my-smtp-server' + port: 587 + secure: false + username: 'my-username' + password: 'my-password' + # The email sender address + sender: 'sender@mycompany.com' + replyTo: 'no-reply@mycompany.com' + # Who to get email for broadcast notifications + broadcastConfig: + receiver: 'users' + # How many emails to send concurrently, defaults to 2 + concurrencyLimit: 10 + # Cache configuration for email addresses + # This is to prevent unnecessary calls to the catalog + cache: + ttl: + days: 1 +``` + +See `config.d.ts` for more options for configuration. diff --git a/plugins/notifications-backend-module-email/api-report.md b/plugins/notifications-backend-module-email/api-report.md new file mode 100644 index 0000000000..26563bde85 --- /dev/null +++ b/plugins/notifications-backend-module-email/api-report.md @@ -0,0 +1,32 @@ +## API Report File for "@backstage/plugin-notifications-backend-module-email" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; +import { ExtensionPoint } from '@backstage/backend-plugin-api'; +import { Notification as Notification_2 } from '@backstage/plugin-notifications-common'; + +// @public (undocumented) +export interface NotificationsEmailTemplateExtensionPoint { + // (undocumented) + setTemplateRenderer(renderer: NotificationTemplateRenderer): void; +} + +// @public (undocumented) +export const notificationsEmailTemplateExtensionPoint: ExtensionPoint<NotificationsEmailTemplateExtensionPoint>; + +// @public (undocumented) +const notificationsModuleEmail: () => BackendFeature; +export default notificationsModuleEmail; + +// @public (undocumented) +export interface NotificationTemplateRenderer { + // (undocumented) + getHtml?(notification: Notification_2): string; + // (undocumented) + getSubject?(notification: Notification_2): string; + // (undocumented) + getText?(notification: Notification_2): string; +} +``` diff --git a/plugins/notifications-backend-module-email/catalog-info.yaml b/plugins/notifications-backend-module-email/catalog-info.yaml new file mode 100644 index 0000000000..6e9c0a044f --- /dev/null +++ b/plugins/notifications-backend-module-email/catalog-info.yaml @@ -0,0 +1,12 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-notifications-backend-module-email + title: '@backstage/plugin-notifications-backend-module-email' + description: >- + The email-notification-processor backend module for the notifications + plugin. +spec: + lifecycle: experimental + type: backstage-backend-plugin-module + owner: maintainers diff --git a/plugins/notifications-backend-module-email/config.d.ts b/plugins/notifications-backend-module-email/config.d.ts new file mode 100644 index 0000000000..db49d0016a --- /dev/null +++ b/plugins/notifications-backend-module-email/config.d.ts @@ -0,0 +1,123 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { HumanDuration } from '@backstage/types'; + +export interface Config { + /** + * Configuration options for notifications-backend-module-email */ + notifications: { + processors: { + email: { + /** + * Transport to use for sending emails */ + transportConfig: + | { + transport: 'smtp'; + /** + * SMTP server hostname + */ + hostname: string; + /** + * SMTP server port + */ + port: number; + /** + * Use secure connection for SMTP, defaults to false + */ + secure?: boolean; + /** + * Require TLS for SMTP connection, defaults to false + */ + requireTls?: boolean; + /** + * SMTP username + */ + username?: string; + /** + * SMTP password + * @visibility secret + */ + password?: string; + } + | { + transport: 'ses'; + /** + * SES ApiVersion to use, defaults to 2010-12-01 + */ + apiVersion?: string; + /** + * AWS account ID to use + */ + accountId?: string; + /** + * AWS region to use + */ + region?: string; + } + | { + transport: 'sendmail'; + /** + * Sendmail binary path, defaults to /usr/sbin/sendmail + */ + path?: string; + /** + * Newline style, defaults to 'unix' + */ + newline?: 'unix' | 'windows'; + }; + /** + * Sender email address + */ + sender: string; + /** + * Optional reply-to address + */ + replyTo?: string; + /** + * Concurrency limit for email sending, defaults to 2 + */ + concurrencyLimit?: number; + /** + * Throttle duration between email sending, defaults to 100ms + */ + throttleInterval?: HumanDuration; + /** + * Configuration for broadcast notifications + */ + broadcastConfig?: { + /** + * Receiver of the broadcast notifications: + * none - skips sending + * users - sends to all users in backstage, might have performance impact + * config - sends to the emails specified in the config + */ + receiver: 'none' | 'users' | 'config'; + /** + * Broadcast notification receivers when receiver is set to config + */ + receiverEmails?: string[]; + }; + cache?: { + /** + * Email cache TTL, defaults to 1 hour + */ + ttl?: HumanDuration; + }; + }; + }; + }; +} diff --git a/plugins/notifications-backend-module-email/package.json b/plugins/notifications-backend-module-email/package.json new file mode 100644 index 0000000000..bb99d9cb97 --- /dev/null +++ b/plugins/notifications-backend-module-email/package.json @@ -0,0 +1,56 @@ +{ + "name": "@backstage/plugin-notifications-backend-module-email", + "version": "0.0.1-next.0", + "description": "The email backend module for the notifications plugin.", + "backstage": { + "role": "backend-plugin-module" + }, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/notifications-backend-module-email" + }, + "license": "Apache-2.0", + "main": "src/index.ts", + "types": "src/index.ts", + "files": [ + "dist", + "config.d.ts" + ], + "scripts": { + "build": "backstage-cli package build", + "clean": "backstage-cli package clean", + "lint": "backstage-cli package lint", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "start": "backstage-cli package start", + "test": "backstage-cli package test" + }, + "dependencies": { + "@aws-sdk/client-ses": "^3.550.0", + "@aws-sdk/types": "^3.347.0", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/integration-aws-node": "workspace:^", + "@backstage/plugin-notifications-common": "workspace:^", + "@backstage/plugin-notifications-node": "workspace:^", + "@backstage/types": "workspace:^", + "lodash": "^4.17.21", + "nodemailer": "^6.9.13", + "p-throttle": "^4.1.1" + }, + "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "@types/nodemailer": "^6.4.14" + }, + "configSchema": "config.d.ts" +} diff --git a/plugins/notifications-backend-module-email/src/extensions.ts b/plugins/notifications-backend-module-email/src/extensions.ts new file mode 100644 index 0000000000..454fe9b384 --- /dev/null +++ b/plugins/notifications-backend-module-email/src/extensions.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createExtensionPoint } from '@backstage/backend-plugin-api'; +import { Notification } from '@backstage/plugin-notifications-common'; + +/** + * @public + */ +export interface NotificationTemplateRenderer { + getSubject?(notification: Notification): string; + getText?(notification: Notification): string; + getHtml?(notification: Notification): string; +} + +/** + * @public + */ +export interface NotificationsEmailTemplateExtensionPoint { + setTemplateRenderer(renderer: NotificationTemplateRenderer): void; +} + +/** + * @public + */ +export const notificationsEmailTemplateExtensionPoint = + createExtensionPoint<NotificationsEmailTemplateExtensionPoint>({ + id: 'notifications.email.templates', + }); diff --git a/plugins/notifications-backend-module-email/src/index.ts b/plugins/notifications-backend-module-email/src/index.ts new file mode 100644 index 0000000000..547a69360c --- /dev/null +++ b/plugins/notifications-backend-module-email/src/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The email backend module for the notifications plugin. + * + * @packageDocumentation + */ + +export { notificationsModuleEmail as default } from './module'; +export * from './extensions'; diff --git a/plugins/notifications-backend-module-email/src/module.ts b/plugins/notifications-backend-module-email/src/module.ts new file mode 100644 index 0000000000..6b74bf70e7 --- /dev/null +++ b/plugins/notifications-backend-module-email/src/module.ts @@ -0,0 +1,72 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { CatalogClient } from '@backstage/catalog-client'; +import { notificationsProcessingExtensionPoint } from '@backstage/plugin-notifications-node'; +import { NotificationsEmailProcessor } from './processor'; +import { + notificationsEmailTemplateExtensionPoint, + NotificationTemplateRenderer, +} from './extensions'; + +/** + * @public + */ +export const notificationsModuleEmail = createBackendModule({ + pluginId: 'notifications', + moduleId: 'email', + register(reg) { + let templateRenderer: NotificationTemplateRenderer | undefined; + reg.registerExtensionPoint(notificationsEmailTemplateExtensionPoint, { + setTemplateRenderer(renderer) { + if (templateRenderer) { + throw new Error(`Email template renderer was already registered`); + } + templateRenderer = renderer; + }, + }); + + reg.registerInit({ + deps: { + config: coreServices.rootConfig, + notifications: notificationsProcessingExtensionPoint, + discovery: coreServices.discovery, + logger: coreServices.logger, + auth: coreServices.auth, + cache: coreServices.cache, + }, + async init({ config, notifications, discovery, logger, auth, cache }) { + const catalogClient = new CatalogClient({ + discoveryApi: discovery, + }); + + notifications.addProcessor( + new NotificationsEmailProcessor( + logger, + config, + catalogClient, + auth, + cache, + templateRenderer, + ), + ); + }, + }); + }, +}); diff --git a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts new file mode 100644 index 0000000000..79d1110968 --- /dev/null +++ b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts @@ -0,0 +1,452 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { mockServices } from '@backstage/backend-test-utils'; +import { NotificationsEmailProcessor } from './NotificationsEmailProcessor'; +import { ConfigReader } from '@backstage/config'; +import { JsonArray } from '@backstage/types'; +import { CatalogClient } from '@backstage/catalog-client'; +import { createTransport } from 'nodemailer'; + +const sendmailMock = jest.fn(); +const mockTransport = { + sendMail: sendmailMock, +}; +jest.mock('nodemailer', () => ({ + ...jest.requireActual('nodemailer'), + createTransport: jest.fn(), +})); + +const DEFAULT_ENTITIES_RESPONSE = { + items: [ + { + kind: 'User', + spec: { + profile: { + email: 'mock@backstage.io', + }, + }, + }, + ], +}; + +const DEFAULT_SENDMAIL_CONFIG = { + app: { + baseUrl: 'https://example.org', + }, + notifications: { + processors: { + email: { + transportConfig: { + transport: 'sendmail', + path: '/usr/local/bin/sendmail', + }, + sender: 'backstage@backstage.io', + }, + }, + }, +}; + +describe('NotificationsEmailProcessor', () => { + const logger = mockServices.logger.mock(); + const auth = mockServices.auth(); + + const getEntityRefMock = jest.fn(); + const getEntitiesMock = jest.fn(); + const mockCatalogClient: Partial<CatalogClient> = { + getEntityByRef: getEntityRefMock, + getEntities: getEntitiesMock, + }; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should create smtp transport', async () => { + const processor = new NotificationsEmailProcessor( + logger, + new ConfigReader({ + app: { + baseUrl: 'http://localhost:3000', + externalBaseUrl: 'https://example.org', + }, + notifications: { + processors: { + email: { + transportConfig: { + transport: 'smtp', + hostname: 'localhost', + port: 465, + secure: true, + requireTls: false, + }, + sender: 'backstage@backstage.io', + }, + }, + }, + }), + mockCatalogClient as unknown as CatalogClient, + auth, + ); + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: 'user:default/mock', + created: new Date(), + payload: { title: 'notification' }, + }, + { + recipients: { type: 'entity', entityRef: 'user:default/mock' }, + payload: { title: 'notification' }, + }, + ); + + expect(processor).toBeInstanceOf(NotificationsEmailProcessor); + expect(createTransport as jest.Mock).toHaveBeenCalledWith({ + host: 'localhost', + port: 465, + requireTLS: false, + secure: true, + }); + }); + + it('should create ses transport', async () => { + const processor = new NotificationsEmailProcessor( + logger, + new ConfigReader({ + app: { + baseUrl: 'http://localhost:3000', + externalBaseUrl: 'https://example.org', + }, + notifications: { + processors: { + email: { + transportConfig: { + transport: 'ses', + region: 'us-west-2', + }, + sender: 'backstage@backstage.io', + }, + }, + }, + }), + mockCatalogClient as unknown as CatalogClient, + auth, + ); + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: 'user:default/mock', + created: new Date(), + payload: { title: 'notification' }, + }, + { + recipients: { type: 'entity', entityRef: 'user:default/mock' }, + payload: { title: 'notification' }, + }, + ); + + expect(processor).toBeInstanceOf(NotificationsEmailProcessor); + expect(createTransport as jest.Mock).toHaveBeenCalledWith({ + SES: expect.anything(), + }); + }); + + it('should create sendmail transport', async () => { + const processor = new NotificationsEmailProcessor( + logger, + new ConfigReader({ + app: { + baseUrl: 'http://localhost:3000', + externalBaseUrl: 'https://example.org', + }, + notifications: { + processors: { + email: { + transportConfig: { + transport: 'sendmail', + path: '/usr/local/bin/sendmail', + }, + sender: 'backstage@backstage.io', + }, + }, + }, + }), + mockCatalogClient as unknown as CatalogClient, + auth, + ); + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: 'user:default/mock', + created: new Date(), + payload: { title: 'notification' }, + }, + { + recipients: { type: 'entity', entityRef: 'user:default/mock' }, + payload: { title: 'notification' }, + }, + ); + + expect(processor).toBeInstanceOf(NotificationsEmailProcessor); + expect(createTransport as jest.Mock).toHaveBeenCalledWith({ + sendmail: true, + path: '/usr/local/bin/sendmail', + newline: 'unix', + }); + }); + + it('should send user email', async () => { + (createTransport as jest.Mock).mockReturnValue(mockTransport); + getEntityRefMock.mockResolvedValue(DEFAULT_ENTITIES_RESPONSE.items[0]); + const processor = new NotificationsEmailProcessor( + logger, + mockServices.rootConfig({ data: DEFAULT_SENDMAIL_CONFIG }), + mockCatalogClient as unknown as CatalogClient, + auth, + ); + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: 'user:default/mock', + created: new Date(), + payload: { title: 'notification' }, + }, + { + recipients: { type: 'entity', entityRef: 'user:default/mock' }, + payload: { title: 'notification' }, + }, + ); + + expect(sendmailMock).toHaveBeenCalledWith({ + from: 'backstage@backstage.io', + html: '<p><a href="https://example.org/notifications">https://example.org/notifications</a></p>', + replyTo: undefined, + subject: 'notification', + text: 'https://example.org/notifications', + to: 'mock@backstage.io', + }); + }); + + it('should send email to all', async () => { + (createTransport as jest.Mock).mockReturnValue(mockTransport); + getEntitiesMock.mockResolvedValue(DEFAULT_ENTITIES_RESPONSE); + const processor = new NotificationsEmailProcessor( + logger, + mockServices.rootConfig({ + data: { + ...DEFAULT_SENDMAIL_CONFIG, + notifications: { + processors: { + email: { + ...DEFAULT_SENDMAIL_CONFIG.notifications.processors.email, + broadcastConfig: { + receiver: 'users', + }, + }, + }, + }, + }, + }), + mockCatalogClient as unknown as CatalogClient, + auth, + ); + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: null, + created: new Date(), + payload: { title: 'notification' }, + }, + { + recipients: { type: 'broadcast' }, + payload: { title: 'notification' }, + }, + ); + + expect(sendmailMock).toHaveBeenCalledWith({ + from: 'backstage@backstage.io', + html: '<p><a href="https://example.org/notifications">https://example.org/notifications</a></p>', + replyTo: undefined, + subject: 'notification', + text: 'https://example.org/notifications', + to: 'mock@backstage.io', + }); + }); + + it('should send email to configured addresses', async () => { + (createTransport as jest.Mock).mockReturnValue(mockTransport); + getEntitiesMock.mockResolvedValue(DEFAULT_ENTITIES_RESPONSE); + const processor = new NotificationsEmailProcessor( + logger, + mockServices.rootConfig({ + data: { + ...DEFAULT_SENDMAIL_CONFIG, + notifications: { + processors: { + email: { + ...DEFAULT_SENDMAIL_CONFIG.notifications.processors.email, + broadcastConfig: { + receiver: 'config', + receiverEmails: ['broadcast@backstage.io'] as JsonArray, + }, + }, + }, + }, + }, + }), + mockCatalogClient as unknown as CatalogClient, + auth, + ); + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: null, + created: new Date(), + payload: { title: 'notification' }, + }, + { + recipients: { type: 'broadcast' }, + payload: { title: 'notification' }, + }, + ); + + expect(sendmailMock).toHaveBeenCalledWith({ + from: 'backstage@backstage.io', + html: '<p><a href="https://example.org/notifications">https://example.org/notifications</a></p>', + replyTo: undefined, + subject: 'notification', + text: 'https://example.org/notifications', + to: 'broadcast@backstage.io', + }); + }); + + it('should send email with relative link to given address', async () => { + (createTransport as jest.Mock).mockReturnValue(mockTransport); + getEntityRefMock.mockResolvedValue(DEFAULT_ENTITIES_RESPONSE.items[0]); + const processor = new NotificationsEmailProcessor( + logger, + mockServices.rootConfig({ + data: DEFAULT_SENDMAIL_CONFIG, + }), + mockCatalogClient as unknown as CatalogClient, + auth, + ); + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: 'user:default/mock', + created: new Date(), + payload: { + title: 'notification', + link: 'catalog/user/default/john.doe', + }, + }, + { + recipients: { type: 'entity', entityRef: 'user:default/mock' }, + payload: { title: 'notification' }, + }, + ); + + expect(sendmailMock).toHaveBeenCalledWith({ + from: 'backstage@backstage.io', + html: '<p><a href="https://example.org/catalog/user/default/john.doe">https://example.org/catalog/user/default/john.doe</a></p>', + replyTo: undefined, + subject: 'notification', + text: 'https://example.org/catalog/user/default/john.doe', + to: 'mock@backstage.io', + }); + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: 'user:default/mock', + created: new Date(), + payload: { + title: 'notification', + link: '/catalog/user/default/jane.doe', + }, + }, + { + recipients: { type: 'entity', entityRef: 'user:default/mock' }, + payload: { title: 'notification' }, + }, + ); + + expect(sendmailMock).toHaveBeenCalledWith({ + from: 'backstage@backstage.io', + html: '<p><a href="https://example.org/catalog/user/default/jane.doe">https://example.org/catalog/user/default/jane.doe</a></p>', + replyTo: undefined, + subject: 'notification', + text: 'https://example.org/catalog/user/default/jane.doe', + to: 'mock@backstage.io', + }); + }); + + it('should send email with absolute link to given address', async () => { + (createTransport as jest.Mock).mockReturnValue(mockTransport); + getEntityRefMock.mockResolvedValue(DEFAULT_ENTITIES_RESPONSE.items[0]); + const processor = new NotificationsEmailProcessor( + logger, + mockServices.rootConfig({ + data: DEFAULT_SENDMAIL_CONFIG, + }), + mockCatalogClient as unknown as CatalogClient, + auth, + ); + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: 'user:default/mock', + created: new Date(), + payload: { + title: 'notification', + link: 'https://backstage.io', + }, + }, + { + recipients: { type: 'entity', entityRef: 'user:default/mock' }, + payload: { title: 'notification' }, + }, + ); + + expect(sendmailMock).toHaveBeenCalledWith({ + from: 'backstage@backstage.io', + html: '<p><a href="https://backstage.io/">https://backstage.io/</a></p>', + replyTo: undefined, + subject: 'notification', + text: 'https://backstage.io/', + to: 'mock@backstage.io', + }); + }); +}); diff --git a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts new file mode 100644 index 0000000000..c06d28f903 --- /dev/null +++ b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts @@ -0,0 +1,315 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + NotificationProcessor, + NotificationSendOptions, +} from '@backstage/plugin-notifications-node'; +import { + AuthService, + CacheService, + LoggerService, +} from '@backstage/backend-plugin-api'; +import { Config, readDurationFromConfig } from '@backstage/config'; +import { durationToMilliseconds } from '@backstage/types'; +import { + CATALOG_FILTER_EXISTS, + CatalogClient, +} from '@backstage/catalog-client'; +import { Notification } from '@backstage/plugin-notifications-common'; +import { + createSendmailTransport, + createSesTransport, + createSmtpTransport, +} from './transports'; +import { UserEntity } from '@backstage/catalog-model'; +import { compact } from 'lodash'; +import { DefaultAwsCredentialsManager } from '@backstage/integration-aws-node'; +import { NotificationTemplateRenderer } from '../extensions'; +import Mail from 'nodemailer/lib/mailer'; +import pThrottle from 'p-throttle'; + +export class NotificationsEmailProcessor implements NotificationProcessor { + private transporter: any; + private readonly broadcastConfig?: Config; + private readonly transportConfig: Config; + private readonly sender: string; + private readonly replyTo?: string; + private readonly cacheTtl: number; + private readonly concurrencyLimit: number; + private readonly throttleInterval: number; + private readonly frontendBaseUrl: string; + + constructor( + private readonly logger: LoggerService, + private readonly config: Config, + private readonly catalog: CatalogClient, + private readonly auth: AuthService, + private readonly cache?: CacheService, + private readonly templateRenderer?: NotificationTemplateRenderer, + ) { + const emailProcessorConfig = config.getConfig( + 'notifications.processors.email', + ); + this.transportConfig = emailProcessorConfig.getConfig('transportConfig'); + this.broadcastConfig = + emailProcessorConfig.getOptionalConfig('broadcastConfig'); + this.sender = emailProcessorConfig.getString('sender'); + this.replyTo = emailProcessorConfig.getOptionalString('replyTo'); + this.concurrencyLimit = + emailProcessorConfig.getOptionalNumber('concurrencyLimit') ?? 2; + const throttleConfig = + emailProcessorConfig.getOptionalConfig('throttleInterval'); + this.throttleInterval = throttleConfig + ? durationToMilliseconds(readDurationFromConfig(throttleConfig)) + : 100; + const cacheConfig = emailProcessorConfig.getOptionalConfig('cache.ttl'); + this.cacheTtl = cacheConfig + ? durationToMilliseconds(readDurationFromConfig(cacheConfig)) + : 3_600_000; + this.frontendBaseUrl = config.getString('app.baseUrl'); + } + + private async getTransporter() { + if (this.transporter) { + return this.transporter; + } + const transport = this.transportConfig.getString('transport'); + if (transport === 'smtp') { + this.transporter = createSmtpTransport(this.transportConfig); + } else if (transport === 'ses') { + const awsCredentialsManager = DefaultAwsCredentialsManager.fromConfig( + this.config, + ); + this.transporter = await createSesTransport( + this.transportConfig, + awsCredentialsManager, + ); + } else if (transport === 'sendmail') { + this.transporter = createSendmailTransport(this.transportConfig); + } else { + throw new Error(`Unsupported transport: ${transport}`); + } + return this.transporter; + } + + getName(): string { + return 'Email'; + } + + private async getBroadcastEmails(): Promise<string[]> { + if (!this.broadcastConfig) { + return []; + } + + const receiver = this.broadcastConfig.getString('receiver'); + if (receiver === 'none') { + return []; + } + + if (receiver === 'config') { + return ( + this.broadcastConfig.getOptionalStringArray('receiverEmails') ?? [] + ); + } + + if (receiver === 'users') { + const cached = await this.cache?.get<string[]>('user-emails:all'); + if (cached) { + return cached; + } + + const { token } = await this.auth.getPluginRequestToken({ + onBehalfOf: await this.auth.getOwnServiceCredentials(), + targetPluginId: 'catalog', + }); + const entities = await this.catalog.getEntities( + { + filter: [ + { kind: 'user', 'spec.profile.email': CATALOG_FILTER_EXISTS }, + ], + fields: ['spec.profile.email'], + }, + { token }, + ); + const ret = compact([ + ...new Set( + entities.items.map(entity => { + return (entity as UserEntity)?.spec.profile?.email; + }), + ), + ]); + + await this.cache?.set('user-emails:all', ret, { + ttl: this.cacheTtl, + }); + return ret; + } + + throw new Error(`Unsupported broadcast receiver: ${receiver}`); + } + + private async getUserEmail(entityRef: string): Promise<string[]> { + const cached = await this.cache?.get<string[]>(`user-emails:${entityRef}`); + if (cached) { + return cached; + } + + const { token } = await this.auth.getPluginRequestToken({ + onBehalfOf: await this.auth.getOwnServiceCredentials(), + targetPluginId: 'catalog', + }); + const entity = await this.catalog.getEntityByRef(entityRef, { token }); + const ret: string[] = []; + if (entity) { + const userEntity = entity as UserEntity; + if (userEntity.spec.profile?.email) { + ret.push(userEntity.spec.profile.email); + } + } + + await this.cache?.set(`user-emails:${entityRef}`, ret, { + ttl: this.cacheTtl, + }); + + return ret; + } + + private async getRecipientEmails( + notification: Notification, + options: NotificationSendOptions, + ) { + if (options.recipients.type === 'broadcast' || notification.user === null) { + return await this.getBroadcastEmails(); + } + return await this.getUserEmail(notification.user); + } + + private async sendMail(options: Mail.Options) { + try { + await this.transporter.sendMail(options); + } catch (e) { + this.logger.error(`Failed to send email to ${options.to}: ${e}`); + } + } + + private async sendMails(options: Mail.Options, emails: string[]) { + const throttle = pThrottle({ + limit: this.concurrencyLimit, + interval: this.throttleInterval, + }); + + const throttled = throttle((opts: Mail.Options) => this.sendMail(opts)); + await Promise.all( + emails.map(email => throttled({ ...options, to: email })), + ); + } + + private getNotificationLink(notification: Notification) { + if (notification.payload.link) { + const stripLeadingSlash = (s: string) => s.replace(/^\//, ''); + const ensureTrailingSlash = (s: string) => s.replace(/\/?$/, '/'); + + try { + const url = new URL( + stripLeadingSlash(notification.payload.link), + ensureTrailingSlash(this.frontendBaseUrl), + ); + return url.toString(); + } catch (_e) { + // noop: fallback to relative URL + } + return notification.payload.link; + } + return `${this.frontendBaseUrl}/notifications`; + } + + private getHtmlContent(notification: Notification) { + const contentParts: string[] = []; + if (notification.payload.description) { + contentParts.push(`${notification.payload.description}`); + } + const link = this.getNotificationLink(notification); + contentParts.push(`<a href="${link}">${link}</a>`); + return `<p>${contentParts.join('<br/>')}</p>`; + } + + private getTextContent(notification: Notification) { + const contentParts: string[] = []; + if (notification.payload.description) { + contentParts.push(notification.payload.description); + } + contentParts.push(this.getNotificationLink(notification)); + return contentParts.join('\n\n'); + } + + private async sendPlainEmail(notification: Notification, emails: string[]) { + const mailOptions = { + from: this.sender, + subject: notification.payload.title, + html: this.getHtmlContent(notification), + text: this.getTextContent(notification), + replyTo: this.replyTo, + }; + + await this.sendMails(mailOptions, emails); + } + + private async sendTemplateEmail( + notification: Notification, + emails: string[], + ) { + const mailOptions = { + from: this.sender, + subject: + this.templateRenderer?.getSubject?.(notification) ?? + notification.payload.title, + html: this.templateRenderer?.getHtml?.(notification), + text: this.templateRenderer?.getText?.(notification), + replyTo: this.replyTo, + }; + + await this.sendMails(mailOptions, emails); + } + + async postProcess( + notification: Notification, + options: NotificationSendOptions, + ): Promise<void> { + this.transporter = await this.getTransporter(); + + let emails: string[] = []; + try { + emails = await this.getRecipientEmails(notification, options); + } catch (e) { + this.logger.error(`Failed to resolve recipient emails: ${e}`); + return; + } + + if (emails.length === 0) { + this.logger.info( + `No email recipients found for notification: ${notification.id}, skipping`, + ); + return; + } + + if (!this.templateRenderer) { + await this.sendPlainEmail(notification, emails); + return; + } + + await this.sendTemplateEmail(notification, emails); + } +} diff --git a/plugins/catalog-backend/src/ingestion/index.ts b/plugins/notifications-backend-module-email/src/processor/index.ts similarity index 82% rename from plugins/catalog-backend/src/ingestion/index.ts rename to plugins/notifications-backend-module-email/src/processor/index.ts index a6555dd98d..cc5d6a1dc8 100644 --- a/plugins/catalog-backend/src/ingestion/index.ts +++ b/plugins/notifications-backend-module-email/src/processor/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,5 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -export type { LocationAnalyzer } from './types'; +export { NotificationsEmailProcessor } from './NotificationsEmailProcessor'; diff --git a/plugins/notifications-backend-module-email/src/processor/transports/index.ts b/plugins/notifications-backend-module-email/src/processor/transports/index.ts new file mode 100644 index 0000000000..ccea9f77cc --- /dev/null +++ b/plugins/notifications-backend-module-email/src/processor/transports/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { createSmtpTransport } from './smtp'; +export { createSesTransport } from './ses'; +export { createSendmailTransport } from './sendmail'; diff --git a/plugins/notifications-backend-module-email/src/processor/transports/sendmail.ts b/plugins/notifications-backend-module-email/src/processor/transports/sendmail.ts new file mode 100644 index 0000000000..e16ea37ac9 --- /dev/null +++ b/plugins/notifications-backend-module-email/src/processor/transports/sendmail.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createTransport } from 'nodemailer'; +import { Config } from '@backstage/config'; + +export const createSendmailTransport = (config: Config) => { + return createTransport({ + sendmail: true, + newline: config.getOptionalString('newline') ?? 'unix', + path: config.getOptionalString('path') ?? '/usr/sbin/sendmail', + }); +}; diff --git a/plugins/notifications-backend-module-email/src/processor/transports/ses.ts b/plugins/notifications-backend-module-email/src/processor/transports/ses.ts new file mode 100644 index 0000000000..0be23078ab --- /dev/null +++ b/plugins/notifications-backend-module-email/src/processor/transports/ses.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createTransport } from 'nodemailer'; +import { SendRawEmailCommand, SES } from '@aws-sdk/client-ses'; +import { Config } from '@backstage/config'; +import { AwsCredentialsManager } from '@backstage/integration-aws-node'; + +export const createSesTransport = async ( + config: Config, + credentialsManager: AwsCredentialsManager, +) => { + const credentials = await credentialsManager.getCredentialProvider({ + accountId: config.getOptionalString('accountId'), + }); + const ses = new SES([ + { + apiVersion: config.getOptionalString('apiVersion') ?? '2010-12-01', + credentials: credentials.sdkCredentialProvider, + region: config.getOptionalString('region'), + }, + ]); + return createTransport({ + SES: { ses, aws: { SendRawEmailCommand } }, + }); +}; diff --git a/plugins/notifications-backend-module-email/src/processor/transports/smtp.ts b/plugins/notifications-backend-module-email/src/processor/transports/smtp.ts new file mode 100644 index 0000000000..feea417c27 --- /dev/null +++ b/plugins/notifications-backend-module-email/src/processor/transports/smtp.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createTransport } from 'nodemailer'; +import { Config } from '@backstage/config'; + +export const createSmtpTransport = (config: Config) => { + const username = config.getOptionalString('username'); + const password = config.getOptionalString('password'); + + return createTransport({ + host: config.getString('hostname'), + port: config.getNumber('port'), + secure: config.getOptionalBoolean('secure') ?? false, + requireTLS: config.getOptionalBoolean('requireTls') ?? false, + auth: username && password ? { user: username, pass: password } : undefined, + }); +}; diff --git a/plugins/notifications-backend/CHANGELOG.md b/plugins/notifications-backend/CHANGELOG.md index c6f9485286..389fa25f61 100644 --- a/plugins/notifications-backend/CHANGELOG.md +++ b/plugins/notifications-backend/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-notifications-backend +## 0.2.1-next.1 + +### Patch Changes + +- 295c05d: Support for filtering entities from notification recipients after resolving them from the recipients +- cba628a: Add possibility to generate random notifications on the fly in local development +- Updated dependencies + - @backstage/plugin-notifications-node@0.1.4-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-signals-node@0.1.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.2.1-next.0 + +### Patch Changes + +- ee09dfc: Updated documentation for sending messages by external services. +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-notifications-node@0.1.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-events-node@0.3.4-next.0 + - @backstage/plugin-notifications-common@0.0.3 + - @backstage/plugin-signals-node@0.1.4-next.0 + ## 0.2.0 ### Minor Changes diff --git a/plugins/notifications-backend/README.md b/plugins/notifications-backend/README.md index 233d0e5e21..60c9a4e078 100644 --- a/plugins/notifications-backend/README.md +++ b/plugins/notifications-backend/README.md @@ -71,7 +71,19 @@ export const myPlugin = createBackendPlugin({ }); ``` -## Sending notifications +## Sending Notifications By Backend Plugins -To be able to send notifications to users, you have to integrate the `@backstage/plugin-notifications-node` -to your application and plugins. +To be able to send notifications to users by other plugins, you have to integrate the `@backstage/plugin-notifications-node` +to your application and plugins. For the API, please refer documentation there. + +## Sending Notifications By External Services + +External services can create new messages by sending POST request to the REST API. + +To be able to do so, `external access` needs to be enabled as described in the [documentation](https://backstage.io/docs/auth/service-to-service-auth), e.g. via the `static tokens`. + +Once the API can be accessed, the request can look like: + +``` +curl -X POST [YOUR_SERVER_URL]/api/notifications -H "Content-Type: application/json" -H "Authorization: Bearer [BASE64_ENCODED_ACCESS_TOKEN]" -d '{"recipients":{"type":"entity","entityRef":"user:development/guest"},"payload": {"title": "Title of user-targeted external message","description": "The description","link": "http://foo.com/bar","severity": "high","topic": "The topic"}}' +``` diff --git a/plugins/notifications-backend/dev/index.ts b/plugins/notifications-backend/dev/index.ts index fe35606dd2..5ee822c0cf 100644 --- a/plugins/notifications-backend/dev/index.ts +++ b/plugins/notifications-backend/dev/index.ts @@ -20,6 +20,55 @@ import { createBackendPlugin, } from '@backstage/backend-plugin-api'; import { notificationService } from '@backstage/plugin-notifications-node'; +import { errorHandler } from '@backstage/backend-common'; +import { + notificationSeverities, + NotificationSeverity, +} from '@backstage/plugin-notifications-common'; +import express, { Response } from 'express'; +import Router from 'express-promise-router'; + +const randomSeverity = (): NotificationSeverity => { + return notificationSeverities[ + Math.floor(Math.random() * notificationSeverities.length) + ]; +}; + +const notificationTitles = [ + 'You have a new notification', + 'Scaffolder task ended', + 'Your entity has some issues', +]; +const randomTitle = (): string => { + return notificationTitles[ + Math.floor(Math.random() * notificationTitles.length) + ]; +}; + +const notificationDescriptions = [ + 'There is a problem in the backstage', + 'See the sound engineer, please', + undefined, +]; +const randomDescription = (): string | undefined => { + return notificationDescriptions[ + Math.floor(Math.random() * notificationDescriptions.length) + ]; +}; + +const notificationTopics = ['Scaffolder', 'Backstage', 'Entity', undefined]; +const randomTopic = (): string | undefined => { + return notificationTopics[ + Math.floor(Math.random() * notificationTopics.length) + ]; +}; + +const notificationLinks = ['/catalog', '/create/tasks/123456', undefined]; +const randomLink = (): string | undefined => { + return notificationLinks[ + Math.floor(Math.random() * notificationLinks.length) + ]; +}; const notificationsDebug = createBackendPlugin({ pluginId: 'notifications-debug', @@ -27,25 +76,32 @@ const notificationsDebug = createBackendPlugin({ env.registerInit({ deps: { notifications: notificationService, - lifecycle: coreServices.lifecycle, + httpRouter: coreServices.httpRouter, }, - async init({ notifications, lifecycle }) { - let interval: NodeJS.Timeout | undefined; - lifecycle.addStartupHook(async () => { - interval = setInterval(async () => { - await notifications.send({ - recipients: { - type: 'broadcast', - }, - payload: { title: 'Test notification' }, - }); - }, 5000); + async init({ notifications, httpRouter }) { + const router = Router(); + router.use(express.json()); + router.post('/', async (_, res: Response<unknown>) => { + await notifications.send({ + recipients: { + type: 'broadcast', + }, + payload: { + title: randomTitle(), + description: randomDescription(), + topic: randomTopic(), + link: randomLink(), + severity: randomSeverity(), + }, + }); + res.status(200).send({ status: 'ok' }); }); + router.use(errorHandler()); - lifecycle.addShutdownHook(async () => { - if (interval) { - clearInterval(interval); - } + httpRouter.use(router); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', }); }, }); diff --git a/plugins/notifications-backend/package.json b/plugins/notifications-backend/package.json index 73e359bbd4..9f7f427895 100644 --- a/plugins/notifications-backend/package.json +++ b/plugins/notifications-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend", - "version": "0.2.0", + "version": "0.2.1-next.1", "backstage": { "role": "backend-plugin" }, diff --git a/plugins/notifications-backend/src/service/router.ts b/plugins/notifications-backend/src/service/router.ts index 30829a578b..585b1ad104 100644 --- a/plugins/notifications-backend/src/service/router.ts +++ b/plugins/notifications-backend/src/service/router.ts @@ -94,6 +94,7 @@ export async function createRouter( const getUsersForEntityRef = async ( entityRef: string | string[] | null, + excludeEntityRefs: string | string[], ): Promise<string[]> => { const { token } = await auth.getPluginRequestToken({ onBehalfOf: await auth.getOwnServiceCredentials(), @@ -114,13 +115,23 @@ export async function createRouter( }, { token }, ); + + const excluded = Array.isArray(excludeEntityRefs) + ? excludeEntityRefs + : [excludeEntityRefs]; + const mapEntity = async (entity: Entity | undefined): Promise<string[]> => { if (!entity) { return []; } + const currentEntityRef = stringifyEntityRef(entity); + if (excluded.includes(currentEntityRef)) { + return []; + } + if (isUserEntity(entity)) { - return [stringifyEntityRef(entity)]; + return [currentEntityRef]; } else if (isGroupEntity(entity) && entity.relations) { const users = entity.relations .filter(relation => relation.type === RELATION_HAS_MEMBER) @@ -162,6 +173,7 @@ export async function createRouter( const u = await mapEntity(entity); users.push(...u); } + return users; }; @@ -482,7 +494,10 @@ export async function createRouter( const entityRef = recipients.entityRef; try { - users = await getUsersForEntityRef(entityRef); + users = await getUsersForEntityRef( + entityRef, + recipients.excludeEntityRef ?? [], + ); } catch (e) { logger.error(`Failed to resolve notification receivers: ${e}`); throw new InputError('Failed to resolve notification receivers', e); diff --git a/plugins/notifications-node/CHANGELOG.md b/plugins/notifications-node/CHANGELOG.md index 03eecc47bb..2b6321a3f8 100644 --- a/plugins/notifications-node/CHANGELOG.md +++ b/plugins/notifications-node/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-notifications-node +## 0.1.4-next.1 + +### Patch Changes + +- 295c05d: Support for filtering entities from notification recipients after resolving them from the recipients +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-signals-node@0.1.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-notifications-common@0.0.3 + - @backstage/plugin-signals-node@0.1.4-next.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/notifications-node/api-report.md b/plugins/notifications-node/api-report.md index a16c141c41..1914b562d1 100644 --- a/plugins/notifications-node/api-report.md +++ b/plugins/notifications-node/api-report.md @@ -41,6 +41,7 @@ export type NotificationRecipients = | { type: 'entity'; entityRef: string | string[]; + excludeEntityRef?: string | string[]; } | { type: 'broadcast'; diff --git a/plugins/notifications-node/package.json b/plugins/notifications-node/package.json index 62b48dd58b..1abfcc07ba 100644 --- a/plugins/notifications-node/package.json +++ b/plugins/notifications-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-node", - "version": "0.1.3", + "version": "0.1.4-next.1", "description": "Node.js library for the notifications plugin", "backstage": { "role": "node-library" diff --git a/plugins/notifications-node/src/service/DefaultNotificationService.ts b/plugins/notifications-node/src/service/DefaultNotificationService.ts index a041dd0ecf..950f42ae83 100644 --- a/plugins/notifications-node/src/service/DefaultNotificationService.ts +++ b/plugins/notifications-node/src/service/DefaultNotificationService.ts @@ -28,7 +28,16 @@ export type NotificationServiceOptions = { export type NotificationRecipients = | { type: 'entity'; + /** + * Entity references to send the notifications to + */ entityRef: string | string[]; + /** + * Optional entity reference(s) to filter out of the resolved recipients. + * Usually the currently logged-in user for preventing sending notification + * of user action to him/herself. + */ + excludeEntityRef?: string | string[]; } | { type: 'broadcast' }; diff --git a/plugins/notifications/CHANGELOG.md b/plugins/notifications/CHANGELOG.md index bc2fdf4360..78c8c96f1d 100644 --- a/plugins/notifications/CHANGELOG.md +++ b/plugins/notifications/CHANGELOG.md @@ -1,5 +1,34 @@ # @backstage/plugin-notifications +## 0.2.1-next.1 + +### Patch Changes + +- f730c0b: The user can newly mark all unread messages as read at one click. +- bfcb2f1: Allow showing notifications as snackbars in the UI +- f6633ca: Add option to set the notification as read automatically when the notification link is opened +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## 0.2.1-next.0 + +### Patch Changes + +- e6bf85f: Allow overriding `NotificationsPage` page properties +- a42a19b: Empty descriptions are not rendered to improve the look&feel. +- 1bc3b86: Fix to show web notifications even when browser is on foreground. Fix duplicate notifications with multiple tabs. +- f793112: Allow defining `className` and additional properties for `NotificationsSideBarItem` +- e1c7d6e: Fix infinite loop in the notification title counter +- fcda449: The rendered size of a notification is limited for very long descriptions. +- Updated dependencies + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.3 + - @backstage/plugin-signals-react@0.0.3 + ## 0.2.0 ### Minor Changes diff --git a/plugins/notifications/api-report.md b/plugins/notifications/api-report.md index 11c671112a..9240bdb1c2 100644 --- a/plugins/notifications/api-report.md +++ b/plugins/notifications/api-report.md @@ -9,6 +9,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { DiscoveryApi } from '@backstage/core-plugin-api'; import { FetchApi } from '@backstage/core-plugin-api'; +import { IconComponent } from '@backstage/core-plugin-api'; import { JSX as JSX_2 } from 'react'; import { Notification as Notification_2 } from '@backstage/plugin-notifications-common'; import { NotificationSeverity } from '@backstage/plugin-notifications-common'; @@ -73,7 +74,20 @@ export class NotificationsClient implements NotificationsApi { } // @public (undocumented) -export const NotificationsPage: () => JSX_2.Element; +export const NotificationsPage: ( + props?: NotificationsPageProps | undefined, +) => JSX_2.Element; + +// @public (undocumented) +export type NotificationsPageProps = { + markAsReadOnLinkOpen?: boolean; + title?: string; + themeId?: string; + subtitle?: string; + tooltip?: string; + type?: string; + typeLink?: string; +}; // @public (undocumented) export const notificationsPlugin: BackstagePlugin< @@ -87,12 +101,21 @@ export const notificationsPlugin: BackstagePlugin< export const NotificationsSidebarItem: (props?: { webNotificationsEnabled?: boolean; titleCounterEnabled?: boolean; + snackbarEnabled?: boolean; + snackbarAutoHideDuration?: number | null; + className?: string; + icon?: IconComponent; + text?: string; + disableHighlight?: boolean; + noTrack?: boolean; }) => React_2.JSX.Element; // @public (undocumented) export const NotificationsTable: ({ + markAsReadOnLinkOpen, isLoading, notifications, + isUnread, onUpdate, setContainsText, onPageChange, @@ -107,7 +130,9 @@ export type NotificationsTableProps = Pick< TableProps, 'onPageChange' | 'onRowsPerPageChange' | 'page' | 'totalCount' > & { + markAsReadOnLinkOpen?: boolean; isLoading?: boolean; + isUnread: boolean; notifications?: Notification_2[]; onUpdate: () => void; setContainsText: (search: string) => void; @@ -157,8 +182,9 @@ export function useTitleCounter(): { }; // @public (undocumented) -export function useWebNotifications(): { +export function useWebNotifications(enabled: boolean): { sendWebNotification: (options: { + id: string; title: string; description: string; link?: string; diff --git a/plugins/notifications/dev/index.tsx b/plugins/notifications/dev/index.tsx index f4d9176a70..794812ef8f 100644 --- a/plugins/notifications/dev/index.tsx +++ b/plugins/notifications/dev/index.tsx @@ -15,14 +15,37 @@ */ import React from 'react'; import { createDevApp } from '@backstage/dev-utils'; -import { NotificationsPage, notificationsPlugin } from '../src/plugin'; -import { NotificationsSidebarItem } from '../src'; +import { + NotificationsPage, + notificationsPlugin, + NotificationsSidebarItem, +} from '../src'; +import { signalsPlugin } from '@backstage/plugin-signals'; +import { SidebarItem } from '@backstage/core-components'; +import AddAlert from '@material-ui/icons/AddAlert'; createDevApp() .registerPlugin(notificationsPlugin) + .registerPlugin(signalsPlugin) .addPage({ - element: <NotificationsPage />, + element: ( + <NotificationsPage + title="Notifications (debug)" + subtitle="Notifications local development environment to showcase notification capabilities" + /> + ), path: '/notifications', }) - .addSidebarItem(<NotificationsSidebarItem />) + .addSidebarItem(<NotificationsSidebarItem webNotificationsEnabled />) + .addSidebarItem( + <SidebarItem + icon={AddAlert} + text="Random notification" + onClick={() => { + fetch('http://localhost:7007/api/notifications-debug/', { + method: 'POST', + }); + }} + />, + ) .render(); diff --git a/plugins/notifications/package.json b/plugins/notifications/package.json index ee2bdc885a..bdc96a921b 100644 --- a/plugins/notifications/package.json +++ b/plugins/notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications", - "version": "0.2.0", + "version": "0.2.1-next.1", "backstage": { "role": "frontend-plugin" }, @@ -43,6 +43,8 @@ "@material-ui/lab": "^4.0.0-alpha.61", "@types/react": "^16.13.1 || ^17.0.0", "lodash": "^4.17.21", + "material-ui-confirm": "^3.0.12", + "notistack": "^3.0.1", "react-relative-time": "^0.0.9", "react-use": "^17.2.4" }, @@ -50,6 +52,7 @@ "@backstage/cli": "workspace:^", "@backstage/core-app-api": "workspace:^", "@backstage/dev-utils": "workspace:^", + "@backstage/plugin-signals": "workspace:^", "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^15.0.0", diff --git a/plugins/notifications/src/components/NotificationsPage/NotificationsPage.tsx b/plugins/notifications/src/components/NotificationsPage/NotificationsPage.tsx index 3069619eef..d1dde185f2 100644 --- a/plugins/notifications/src/components/NotificationsPage/NotificationsPage.tsx +++ b/plugins/notifications/src/components/NotificationsPage/NotificationsPage.tsx @@ -22,6 +22,7 @@ import { ResponseErrorPanel, } from '@backstage/core-components'; import Grid from '@material-ui/core/Grid'; +import { ConfirmProvider } from 'material-ui-confirm'; import { useSignal } from '@backstage/plugin-signals-react'; import { NotificationsTable } from '../NotificationsTable'; @@ -32,12 +33,37 @@ import { SortBy, SortByOptions, } from '../NotificationsFilters'; -import { GetNotificationsOptions } from '../../api'; -import { NotificationSeverity } from '@backstage/plugin-notifications-common'; +import { GetNotificationsOptions, GetNotificationsResponse } from '../../api'; +import { + NotificationSeverity, + NotificationStatus, +} from '@backstage/plugin-notifications-common'; const ThrottleDelayMs = 2000; -export const NotificationsPage = () => { +/** @public */ +export type NotificationsPageProps = { + /** Mark notification as read when opening the link it contains, defaults to false */ + markAsReadOnLinkOpen?: boolean; + title?: string; + themeId?: string; + subtitle?: string; + tooltip?: string; + type?: string; + typeLink?: string; +}; + +export const NotificationsPage = (props?: NotificationsPageProps) => { + const { + title = 'Notifications', + themeId = 'tool', + subtitle, + tooltip, + type, + typeLink, + markAsReadOnLinkOpen, + } = props ?? {}; + const [refresh, setRefresh] = React.useState(false); const { lastSignal } = useSignal('notifications'); const [unreadOnly, setUnreadOnly] = React.useState<boolean | undefined>(true); @@ -51,7 +77,9 @@ export const NotificationsPage = () => { ); const [severity, setSeverity] = React.useState<NotificationSeverity>('low'); - const { error, value, retry, loading } = useNotificationsApi( + const { error, value, retry, loading } = useNotificationsApi< + [GetNotificationsResponse, NotificationStatus] + >( api => { const options: GetNotificationsOptions = { search: containsText, @@ -72,7 +100,7 @@ export const NotificationsPage = () => { options.createdAfter = createdAfterDate; } - return api.getNotifications(options); + return Promise.all([api.getNotifications(options), api.getStatus()]); }, [ containsText, @@ -112,38 +140,53 @@ export const NotificationsPage = () => { return <ResponseErrorPanel error={error} />; } + const notifications = value?.[0]?.notifications; + const totalCount = value?.[0]?.totalCount; + const isUnread = !!value?.[1]?.unread; + return ( - <PageWithHeader title="Notifications" themeId="tool"> + <PageWithHeader + title={title} + themeId={themeId} + tooltip={tooltip} + subtitle={subtitle} + type={type} + typeLink={typeLink} + > <Content> - <Grid container> - <Grid item xs={2}> - <NotificationsFilters - unreadOnly={unreadOnly} - onUnreadOnlyChanged={setUnreadOnly} - createdAfter={createdAfter} - onCreatedAfterChanged={setCreatedAfter} - onSortingChanged={setSorting} - sorting={sorting} - saved={saved} - onSavedChanged={setSaved} - severity={severity} - onSeverityChanged={setSeverity} - /> + <ConfirmProvider> + <Grid container> + <Grid item xs={2}> + <NotificationsFilters + unreadOnly={unreadOnly} + onUnreadOnlyChanged={setUnreadOnly} + createdAfter={createdAfter} + onCreatedAfterChanged={setCreatedAfter} + onSortingChanged={setSorting} + sorting={sorting} + saved={saved} + onSavedChanged={setSaved} + severity={severity} + onSeverityChanged={setSeverity} + /> + </Grid> + <Grid item xs={10}> + <NotificationsTable + isLoading={loading} + isUnread={isUnread} + markAsReadOnLinkOpen={markAsReadOnLinkOpen} + notifications={notifications} + onUpdate={onUpdate} + setContainsText={setContainsText} + onPageChange={setPageNumber} + onRowsPerPageChange={setPageSize} + page={pageNumber} + pageSize={pageSize} + totalCount={totalCount} + /> + </Grid> </Grid> - <Grid item xs={10}> - <NotificationsTable - isLoading={loading} - notifications={value?.notifications} - onUpdate={onUpdate} - setContainsText={setContainsText} - onPageChange={setPageNumber} - onRowsPerPageChange={setPageSize} - page={pageNumber} - pageSize={pageSize} - totalCount={value?.totalCount} - /> - </Grid> - </Grid> + </ConfirmProvider> </Content> </PageWithHeader> ); diff --git a/plugins/notifications/src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx b/plugins/notifications/src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx index 2163c6660f..7b55c63747 100644 --- a/plugins/notifications/src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx +++ b/plugins/notifications/src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx @@ -13,38 +13,152 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { useNotificationsApi } from '../../hooks'; -import { SidebarItem } from '@backstage/core-components'; +import { Link, SidebarItem } from '@backstage/core-components'; import NotificationsIcon from '@material-ui/icons/Notifications'; -import { useApi, useRouteRef } from '@backstage/core-plugin-api'; +import { + alertApiRef, + IconComponent, + useApi, + useRouteRef, +} from '@backstage/core-plugin-api'; import { rootRouteRef } from '../../routes'; import { useSignal } from '@backstage/plugin-signals-react'; -import { NotificationSignal } from '@backstage/plugin-notifications-common'; +import { + Notification, + NotificationSignal, +} from '@backstage/plugin-notifications-common'; import { useWebNotifications } from '../../hooks/useWebNotifications'; import { useTitleCounter } from '../../hooks/useTitleCounter'; import { notificationsApiRef } from '../../api'; +import { + closeSnackbar, + enqueueSnackbar, + MaterialDesignContent, + OptionsWithExtraProps, + SnackbarKey, + SnackbarProvider, + VariantType, +} from 'notistack'; +import { SeverityIcon } from '../NotificationsTable/SeverityIcon'; +import OpenInNew from '@material-ui/icons/OpenInNew'; +import MarkAsReadIcon from '@material-ui/icons/CheckCircle'; +import IconButton from '@material-ui/core/IconButton'; +import { styled } from '@material-ui/core/styles'; + +const StyledMaterialDesignContent = styled(MaterialDesignContent)( + ({ theme }) => ({ + '&.notistack-MuiContent-low': { + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + }, + '&.notistack-MuiContent-normal': { + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + }, + '&.notistack-MuiContent-high': { + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + }, + '&.notistack-MuiContent-critical': { + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + }, + }), +); + +declare module 'notistack' { + interface VariantOverrides { + // Custom variants for the snackbar + low: true; + normal: true; + high: true; + critical: true; + } +} /** @public */ export const NotificationsSidebarItem = (props?: { webNotificationsEnabled?: boolean; titleCounterEnabled?: boolean; + snackbarEnabled?: boolean; + snackbarAutoHideDuration?: number | null; + className?: string; + icon?: IconComponent; + text?: string; + disableHighlight?: boolean; + noTrack?: boolean; }) => { - const { webNotificationsEnabled = false, titleCounterEnabled = true } = - props ?? { webNotificationsEnabled: false, titleCounterEnabled: true }; + const { + webNotificationsEnabled = false, + titleCounterEnabled = true, + snackbarEnabled = true, + snackbarAutoHideDuration = 10000, + icon = NotificationsIcon, + text = 'Notifications', + ...restProps + } = props ?? { + webNotificationsEnabled: false, + titleCounterEnabled: true, + snackbarEnabled: true, + snackbarAutoHideDuration: 10000, + }; const { loading, error, value, retry } = useNotificationsApi(api => api.getStatus(), ); const notificationsApi = useApi(notificationsApiRef); + const alertApi = useApi(alertApiRef); const [unreadCount, setUnreadCount] = React.useState(0); const notificationsRoute = useRouteRef(rootRouteRef); // TODO: Do we want to add long polling in case signals are not available const { lastSignal } = useSignal<NotificationSignal>('notifications'); - const { sendWebNotification } = useWebNotifications(); + const { sendWebNotification } = useWebNotifications(webNotificationsEnabled); const [refresh, setRefresh] = React.useState(false); const { setNotificationCount } = useTitleCounter(); + const getSnackbarProperties = useCallback( + (notification: Notification) => { + const action = (snackBarId: SnackbarKey) => ( + <> + <IconButton + component={Link} + to={notification.payload.link ?? notificationsRoute()} + onClick={() => { + closeSnackbar(snackBarId); + }} + > + <OpenInNew fontSize="small" /> + </IconButton> + <IconButton + onClick={() => { + notificationsApi + .updateNotifications({ + ids: [notification.id], + read: true, + }) + .then(() => { + closeSnackbar(snackBarId); + }) + .catch(() => { + alertApi.post({ + message: 'Failed to mark notification as read', + severity: 'error', + }); + }); + }} + > + <MarkAsReadIcon fontSize="small" /> + </IconButton> + </> + ); + + return { action }; + }, + [notificationsRoute, notificationsApi, alertApi], + ); + useEffect(() => { if (refresh) { retry(); @@ -53,8 +167,11 @@ export const NotificationsSidebarItem = (props?: { }, [refresh, retry]); useEffect(() => { - const handleWebNotification = (signal: NotificationSignal) => { - if (!webNotificationsEnabled || signal.action !== 'new_notification') { + const handleNotificationSignal = (signal: NotificationSignal) => { + if ( + (!webNotificationsEnabled && !snackbarEnabled) || + signal.action !== 'new_notification' + ) { return; } @@ -64,41 +181,89 @@ export const NotificationsSidebarItem = (props?: { if (!notification) { return; } - sendWebNotification({ - title: notification.payload.title, - description: notification.payload.description ?? '', - link: notification.payload.link, + if (webNotificationsEnabled) { + sendWebNotification({ + id: notification.id, + title: notification.payload.title, + description: notification.payload.description ?? '', + link: notification.payload.link, + }); + } + if (snackbarEnabled) { + const { action } = getSnackbarProperties(notification); + const snackBarText = + notification.payload.title.length > 50 + ? `${notification.payload.title.substring(0, 50)}...` + : notification.payload.title; + enqueueSnackbar(snackBarText, { + variant: notification.payload.severity, + anchorOrigin: { vertical: 'bottom', horizontal: 'right' }, + action, + autoHideDuration: snackbarAutoHideDuration, + } as OptionsWithExtraProps<VariantType>); + } + }) + .catch(() => { + alertApi.post({ + message: 'Failed to fetch notification', + severity: 'error', }); }); }; if (lastSignal && lastSignal.action) { - handleWebNotification(lastSignal); + handleNotificationSignal(lastSignal); setRefresh(true); } }, [ lastSignal, sendWebNotification, webNotificationsEnabled, + snackbarEnabled, + snackbarAutoHideDuration, notificationsApi, + alertApi, + getSnackbarProperties, ]); useEffect(() => { if (!loading && !error && value) { setUnreadCount(value.unread); - if (titleCounterEnabled) { - setNotificationCount(value.unread); - } } - }, [loading, error, value, titleCounterEnabled, setNotificationCount]); + }, [loading, error, value]); + + useEffect(() => { + if (titleCounterEnabled) { + setNotificationCount(unreadCount); + } + }, [titleCounterEnabled, unreadCount, setNotificationCount]); // TODO: Figure out if the count can be added to hasNotifications return ( - <SidebarItem - icon={NotificationsIcon} - to={notificationsRoute()} - text="Notifications" - hasNotifications={!error && !!unreadCount} - /> + <> + {snackbarEnabled && ( + <SnackbarProvider + iconVariant={{ + normal: <SeverityIcon severity="normal" />, + critical: <SeverityIcon severity="critical" />, + high: <SeverityIcon severity="high" />, + low: <SeverityIcon severity="low" />, + }} + Components={{ + normal: StyledMaterialDesignContent, + critical: StyledMaterialDesignContent, + high: StyledMaterialDesignContent, + low: StyledMaterialDesignContent, + }} + /> + )} + <SidebarItem + to={notificationsRoute()} + hasNotifications={!error && !!unreadCount} + text={text} + icon={icon} + {...restProps} + /> + </> ); }; diff --git a/plugins/notifications/src/components/NotificationsTable/BulkActions.tsx b/plugins/notifications/src/components/NotificationsTable/BulkActions.tsx index 034da7c93c..40e2f5c8bb 100644 --- a/plugins/notifications/src/components/NotificationsTable/BulkActions.tsx +++ b/plugins/notifications/src/components/NotificationsTable/BulkActions.tsx @@ -22,17 +22,22 @@ import MarkAsUnreadIcon from '@material-ui/icons/Markunread' /* TODO: use Drafts import MarkAsReadIcon from '@material-ui/icons/CheckCircle'; import MarkAsUnsavedIcon from '@material-ui/icons/LabelOff' /* TODO: use BookmarkRemove and BookmarkAdd once we have mui 5 icons */; import MarkAsSavedIcon from '@material-ui/icons/Label'; +import MarkAllReadIcon from '@material-ui/icons/DoneAll'; export const BulkActions = ({ selectedNotifications, notifications, + isUnread, onSwitchReadStatus, onSwitchSavedStatus, + onMarkAllRead, }: { selectedNotifications: Set<Notification['id']>; notifications: Notification[]; + isUnread?: boolean; onSwitchReadStatus: (ids: Notification['id'][], newStatus: boolean) => void; onSwitchSavedStatus: (ids: Notification['id'][], newStatus: boolean) => void; + onMarkAllRead?: () => void; }) => { const isDisabled = selectedNotifications.size === 0; const bulkNotifications = notifications.filter(notification => @@ -58,7 +63,22 @@ export const BulkActions = ({ return ( <Grid container wrap="nowrap"> - <Grid item> + <Grid item xs={3}> + {onMarkAllRead ? ( + <Tooltip title="Mark all read"> + <div> + {/* The <div> here is a workaround for the Tooltip which does not work for a "disabled" child */} + <IconButton disabled={!isUnread} onClick={onMarkAllRead}> + <MarkAllReadIcon aria-label={markAsSavedText} /> + </IconButton> + </div> + </Tooltip> + ) : ( + <div /> + )} + </Grid> + + <Grid item xs={3}> <Tooltip title={markAsSavedText}> <div> {/* The <div> here is a workaround for the Tooltip which does not work for a "disabled" child */} @@ -74,7 +94,7 @@ export const BulkActions = ({ </Tooltip> </Grid> - <Grid item> + <Grid item xs={3}> <Tooltip title={markAsReadText}> <div> <IconButton diff --git a/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx b/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx index daaa937611..db2b351432 100644 --- a/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx +++ b/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx @@ -21,29 +21,43 @@ import Box from '@material-ui/core/Box'; import Grid from '@material-ui/core/Grid'; import CheckBox from '@material-ui/core/Checkbox'; import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core/styles'; import { Notification } from '@backstage/plugin-notifications-common'; - -import { notificationsApiRef } from '../../api'; -import { useApi } from '@backstage/core-plugin-api'; +import { useConfirm } from 'material-ui-confirm'; +import { alertApiRef, useApi } from '@backstage/core-plugin-api'; import { Link, Table, - TableProps, TableColumn, + TableProps, } from '@backstage/core-components'; +import { notificationsApiRef } from '../../api'; + import { SeverityIcon } from './SeverityIcon'; import { SelectAll } from './SelectAll'; import { BulkActions } from './BulkActions'; const ThrottleDelayMs = 1000; +const useStyles = makeStyles({ + description: { + maxHeight: '5rem', + overflow: 'scroll', + }, + severityItem: { + alignContent: 'center', + }, +}); + /** @public */ export type NotificationsTableProps = Pick< TableProps, 'onPageChange' | 'onRowsPerPageChange' | 'page' | 'totalCount' > & { + markAsReadOnLinkOpen?: boolean; isLoading?: boolean; + isUnread: boolean; notifications?: Notification[]; onUpdate: () => void; setContainsText: (search: string) => void; @@ -52,8 +66,10 @@ export type NotificationsTableProps = Pick< /** @public */ export const NotificationsTable = ({ + markAsReadOnLinkOpen, isLoading, notifications = [], + isUnread, onUpdate, setContainsText, onPageChange, @@ -62,7 +78,11 @@ export const NotificationsTable = ({ pageSize, totalCount, }: NotificationsTableProps) => { + const classes = useStyles(); const notificationsApi = useApi(notificationsApiRef); + const alertApi = useApi(alertApiRef); + const confirm = useConfirm(); + const [selectedNotifications, setSelectedNotifications] = React.useState( new Set<Notification['id']>(), ); @@ -105,6 +125,39 @@ export const NotificationsTable = ({ [notificationsApi, onUpdate], ); + const onMarkAllRead = React.useCallback(() => { + confirm({ + title: 'Are you sure?', + description: ( + <> + Mark <b>all</b> notifications as <b>read</b>. + </> + ), + confirmationText: 'Mark All', + }) + .then(async () => { + const ids = ( + await notificationsApi.getNotifications({ read: false }) + ).notifications?.map(notification => notification.id); + + return notificationsApi + .updateNotifications({ + ids, + read: true, + }) + .then(onUpdate); + }) + .catch(e => { + if (e) { + // if e === undefined, the Cancel button has been hit + alertApi.post({ + message: 'Failed to mark all notifications as read', + severity: 'error', + }); + } + }); + }, [alertApi, confirm, notificationsApi, onUpdate]); + const throttledContainsTextHandler = React.useMemo( () => throttle(setContainsText, ThrottleDelayMs), [setContainsText], @@ -155,23 +208,32 @@ export const NotificationsTable = ({ // Compact content return ( <Grid container> - <Grid item> + <Grid item className={classes.severityItem}> <SeverityIcon severity={notification.payload?.severity} /> </Grid> - <Grid item> + <Grid item xs={11}> <Box> <Typography variant="subtitle2"> {notification.payload.link ? ( - <Link to={notification.payload.link}> + <Link + to={notification.payload.link} + onClick={() => { + if (markAsReadOnLinkOpen && !notification.read) { + onSwitchReadStatus([notification.id], true); + } + }} + > {notification.payload.title} </Link> ) : ( notification.payload.title )} </Typography> - <Typography variant="body2"> - {notification.payload.description} - </Typography> + {notification.payload.description ? ( + <Typography variant="body2" className={classes.description}> + {notification.payload.description} + </Typography> + ) : null} <Typography variant="caption"> {notification.origin && ( <>{notification.origin} • </> @@ -196,8 +258,10 @@ export const NotificationsTable = ({ <BulkActions notifications={notifications} selectedNotifications={selectedNotifications} + isUnread={isUnread} onSwitchReadStatus={onSwitchReadStatus} onSwitchSavedStatus={onSwitchSavedStatus} + onMarkAllRead={onMarkAllRead} /> ), render: (notification: Notification) => ( @@ -206,16 +270,22 @@ export const NotificationsTable = ({ selectedNotifications={new Set([notification.id])} onSwitchReadStatus={onSwitchReadStatus} onSwitchSavedStatus={onSwitchSavedStatus} + // /> ), }, ], [ + markAsReadOnLinkOpen, + selectedNotifications, + notifications, + isUnread, onSwitchReadStatus, onSwitchSavedStatus, - selectedNotifications, + onMarkAllRead, onNotificationsSelectChange, - notifications, + classes.severityItem, + classes.description, ], ); diff --git a/plugins/notifications/src/components/index.ts b/plugins/notifications/src/components/index.ts index 3bca70a215..022ae07198 100644 --- a/plugins/notifications/src/components/index.ts +++ b/plugins/notifications/src/components/index.ts @@ -15,3 +15,4 @@ */ export * from './NotificationsSideBarItem'; export * from './NotificationsTable'; +export type { NotificationsPageProps } from './NotificationsPage'; diff --git a/plugins/notifications/src/hooks/useTitleCounter.ts b/plugins/notifications/src/hooks/useTitleCounter.ts index d4ba22b946..d794678ea6 100644 --- a/plugins/notifications/src/hooks/useTitleCounter.ts +++ b/plugins/notifications/src/hooks/useTitleCounter.ts @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; /** @public */ export function useTitleCounter() { const [title, setTitle] = useState(document.title); const [count, setCount] = useState(0); + const titleTimer = useRef<undefined | number>(undefined); const getPrefix = (value: number) => { return value === 0 ? '' : `(${value}) `; @@ -28,28 +29,43 @@ export function useTitleCounter() { return currentTitle.replace(/^\(\d+\)\s/, ''); }; - useEffect(() => { - document.title = title; - }, [title]); - useEffect(() => { const baseTitle = cleanTitle(title); - setTitle(`${getPrefix(count)}${baseTitle}`); + const shownTitle = `${getPrefix(count)}${baseTitle}`; + if (document.title !== shownTitle) { + window.clearTimeout(titleTimer.current); + document.title = shownTitle; + // Need to do this in timeout as the React Helmet overrides the title after this effect + titleTimer.current = window.setTimeout(() => { + document.title = shownTitle; + }, 50); + } return () => { + window.clearTimeout(titleTimer.current); document.title = cleanTitle(title); }; }, [title, count]); - const titleElement = document.querySelector('title'); - if (titleElement) { - new MutationObserver(() => { - setTitle(document.title); - }).observe(titleElement, { - subtree: true, - characterData: true, - childList: true, - }); - } + useEffect(() => { + const titleElement = document.querySelector('title'); + let observer: MutationObserver | undefined; + if (titleElement) { + observer = new MutationObserver(mutations => { + if (mutations?.[0]?.target?.textContent) { + setTitle(mutations[0].target.textContent); + } + }); + observer.observe(titleElement, { + characterData: true, + childList: true, + }); + } + return () => { + if (observer) { + observer.disconnect(); + } + }; + }, []); const setNotificationCount = useCallback( (newCount: number) => setCount(newCount), diff --git a/plugins/notifications/src/hooks/useWebNotifications.ts b/plugins/notifications/src/hooks/useWebNotifications.ts index bf3b5269f5..728d992130 100644 --- a/plugins/notifications/src/hooks/useWebNotifications.ts +++ b/plugins/notifications/src/hooks/useWebNotifications.ts @@ -14,49 +14,58 @@ * limitations under the License. */ import { useCallback, useEffect, useState } from 'react'; +import { rootRouteRef } from '../routes'; +import { useRouteRef } from '@backstage/core-plugin-api'; +import { useNavigate } from 'react-router-dom'; /** @public */ -export function useWebNotifications() { +export function useWebNotifications(enabled: boolean) { const [webNotificationPermission, setWebNotificationPermission] = useState('default'); - const [webNotifications, setWebNotifications] = useState<Notification[]>([]); + const notificationsRoute = useRouteRef(rootRouteRef); + const navigate = useNavigate(); useEffect(() => { - if ('Notification' in window && webNotificationPermission === 'default') { + if ( + enabled && + 'Notification' in window && + webNotificationPermission === 'default' + ) { window.Notification.requestPermission().then(permission => { setWebNotificationPermission(permission); }); } - }, [webNotificationPermission]); - - document.addEventListener('visibilitychange', () => { - if (document.visibilityState === 'visible') { - webNotifications.forEach(n => n.close()); - setWebNotifications([]); - } - }); + }, [enabled, webNotificationPermission]); const sendWebNotification = useCallback( - (options: { title: string; description: string; link?: string }) => { + (options: { + id: string; + title: string; + description: string; + link?: string; + }) => { if (webNotificationPermission !== 'granted') { return null; } const notification = new Notification(options.title, { body: options.description, + tag: options.id, // Prevent duplicates from multiple tabs }); notification.onclick = event => { event.preventDefault(); - notification.close(); if (options.link) { window.open(options.link, '_blank'); + } else { + navigate(notificationsRoute()); } + notification.close(); }; return notification; }, - [webNotificationPermission], + [webNotificationPermission, navigate, notificationsRoute], ); return { sendWebNotification }; diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index 8e386db176..2ad74d1db0 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-org-react +## 0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + +## 0.1.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/core-plugin-api@1.9.2 + ## 0.1.23 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index b56d916e89..0567691a3b 100644 --- a/plugins/org-react/package.json +++ b/plugins/org-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org-react", - "version": "0.1.23", + "version": "0.1.24-next.1", "backstage": { "role": "web-library" }, diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 72402286ba..3d2bedca68 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-org +## 0.6.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## 0.6.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.5-next.0 + ## 0.6.24 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index 173495d846..b00e6f2bb2 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.6.24", + "version": "0.6.25-next.1", "description": "A Backstage plugin that helps you create entity pages for your organization", "backstage": { "role": "frontend-plugin" diff --git a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md index 31d745e2f2..90874346ad 100644 --- a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md +++ b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-permission-backend-module-allow-all-policy +## 0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + ## 0.1.14 ### Patch Changes diff --git a/plugins/permission-backend-module-policy-allow-all/package.json b/plugins/permission-backend-module-policy-allow-all/package.json index 6652418c00..ef5dfade7f 100644 --- a/plugins/permission-backend-module-policy-allow-all/package.json +++ b/plugins/permission-backend-module-policy-allow-all/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend-module-allow-all-policy", - "version": "0.1.14", + "version": "0.1.15-next.1", "description": "Allow all policy backend module for the permission plugin.", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/permission-backend/CHANGELOG.md b/plugins/permission-backend/CHANGELOG.md index 95934c32be..cc5b497a2c 100644 --- a/plugins/permission-backend/CHANGELOG.md +++ b/plugins/permission-backend/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-permission-backend +## 0.5.42-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.5.42-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + ## 0.5.41 ### Patch Changes diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index 320c52288a..3671e2b5a4 100644 --- a/plugins/permission-backend/package.json +++ b/plugins/permission-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend", - "version": "0.5.41", + "version": "0.5.42-next.1", "backstage": { "role": "backend-plugin" }, diff --git a/plugins/permission-node/CHANGELOG.md b/plugins/permission-node/CHANGELOG.md index 2e83533229..fd84633615 100644 --- a/plugins/permission-node/CHANGELOG.md +++ b/plugins/permission-node/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-permission-node +## 0.7.29-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.7.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.7.13 + ## 0.7.28 ### Patch Changes diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index edef0fd002..c510014089 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-permission-node", "description": "Common permission and authorization utilities for backend plugins", - "version": "0.7.28", + "version": "0.7.29-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index c42522de43..b133154c85 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-proxy-backend +## 0.4.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.4.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + ## 0.4.15 ### Patch Changes diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index ad9dc04681..9430f7120d 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-backend", - "version": "0.4.15", + "version": "0.4.16-next.1", "description": "A Backstage backend plugin that helps you set up proxy endpoints in the backend", "backstage": { "role": "backend-plugin" @@ -37,8 +37,7 @@ }, "files": [ "dist", - "config.d.ts", - "alpha" + "config.d.ts" ], "scripts": { "build": "backstage-cli package build", diff --git a/plugins/scaffolder-backend-module-azure/CHANGELOG.md b/plugins/scaffolder-backend-module-azure/CHANGELOG.md index f0df53a3ce..37987ca19f 100644 --- a/plugins/scaffolder-backend-module-azure/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-azure/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-scaffolder-backend-module-azure +## 0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + ## 0.1.9 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-azure/package.json b/plugins/scaffolder-backend-module-azure/package.json index 4689ad1130..efd61bae83 100644 --- a/plugins/scaffolder-backend-module-azure/package.json +++ b/plugins/scaffolder-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-azure", - "version": "0.1.9", + "version": "0.1.10-next.1", "description": "The azure module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md index 4f4afff38b..099d4d6fd0 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-cloud +## 0.1.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + ## 0.1.7 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json index 03200fb368..51df2f87ed 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud", - "version": "0.1.7", + "version": "0.1.8-next.1", "description": "The Bitbucket Cloud module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md index 9024c6c826..83a345f463 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-server +## 0.1.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.8-next.0 + +### Patch Changes + +- 24dd655: Add examples for `publish:bitbucketServer:pull-request` scaffolder action & improve related tests +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + ## 0.1.7 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-server/package.json b/plugins/scaffolder-backend-module-bitbucket-server/package.json index 1bd3a5a1e1..2c195c8d88 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket-server", - "version": "0.1.7", + "version": "0.1.8-next.1", "description": "The Bitbucket Server module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md index be4e753d60..31b5cfad46 100644 --- a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket +## 0.2.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.8-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.8-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.8-next.0 + ## 0.2.7 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket/package.json b/plugins/scaffolder-backend-module-bitbucket/package.json index 4a1cfb0ee8..ad07c7208d 100644 --- a/plugins/scaffolder-backend-module-bitbucket/package.json +++ b/plugins/scaffolder-backend-module-bitbucket/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket", - "version": "0.2.7", + "version": "0.2.8-next.1", "description": "The bitbucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md index cdf261d8d8..1bbc8a234d 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-scaffolder-backend-module-confluence-to-markdown +## 0.2.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + ## 0.2.18 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json index 556fb2eb22..ddf56c91d1 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown", - "version": "0.2.18", + "version": "0.2.19-next.1", "description": "The confluence-to-markdown module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index dbda392208..01535ca917 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.2.42-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.2.42-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/types@1.1.1 + ## 0.2.41 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index d1b867cae7..b4206baff2 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-cookiecutter", - "version": "0.2.41", + "version": "0.2.42-next.1", "description": "A module for the scaffolder backend that lets you template projects using cookiecutter", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md index 2b176d24d7..959d01271b 100644 --- a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-scaffolder-backend-module-gerrit +## 0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + ## 0.1.9 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gerrit/package.json b/plugins/scaffolder-backend-module-gerrit/package.json index 96a0ff2c47..d7f9bfce66 100644 --- a/plugins/scaffolder-backend-module-gerrit/package.json +++ b/plugins/scaffolder-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gerrit", - "version": "0.1.9", + "version": "0.1.10-next.1", "description": "The gerrit module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md index f3ae9326d6..306fbd6812 100644 --- a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-scaffolder-backend-module-gitea +## 0.1.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.8-next.0 + +### Patch Changes + +- 554af73: Allow defining `repoVisibility` field for the action `publish:gitea` +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + ## 0.1.7 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitea/README.md b/plugins/scaffolder-backend-module-gitea/README.md index 7eff6130db..5c6aa59b66 100644 --- a/plugins/scaffolder-backend-module-gitea/README.md +++ b/plugins/scaffolder-backend-module-gitea/README.md @@ -10,6 +10,13 @@ To use this action, you will have to add the package using the following command yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-gitea ``` +Alternatively, if you use the new backend system, then register it like this: + +```typescript +// packages/backend/src/index.ts +backend.add(import('@backstage/plugin-scaffolder-backend-module-gitea')); +``` + Configure the action (if not yet done): (you can check the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to see all options): @@ -27,29 +34,29 @@ integrations: password: '<GITEA_LOCALHOST_PASSWORD>' ``` -**Important**: As backstage will issue HTTPS/TLS requests to the gitea instance, it is needed to configure `gitea` with a valid certificate or at least with a -self-signed certificate `gitea cert --host localhost -ca` trusted by a CA authority. Don't forget to set the env var `NODE_EXTRA_CA_CERTS` to point to the CA file before launching backstage ! +**Important**: As backstage will issue `HTTPS/TLS` requests to the gitea instance, it is needed to configure `gitea` with a valid certificate or at least with a +self-signed certificate `gitea cert --host localhost -ca` trusted by a CA authority. Don't forget to set the env var `NODE_EXTRA_CA_CERTS` to point to the CA file before launching backstage or you can set temporarily `NODE_TLS_REJECT_UNAUTHORIZED=0` but this is not recommended for production! When done, you can create a template which: -- Declare the `RepoUrlPicker` within the `spec/parameters` section to select the gitea hosts -- Include a step able to publish by example the newly generated project using the action `action: publish:gitea` +- Declare the `RepoUrlPicker` within the `spec/parameters` section to select the gitea host and to provide the name of the repository +- Add an `enum` list allowing the user to define the visibility about the repository to be created: `public` or `private`. If this field is omitted, `public` is then used by the action. +- Include in a step the action: `publish:gitea` + +**Warning**: The list of the `allowedOwners` of the `repoUrlPicker` must match the list of the `organizations` which are available on the gitea host ! ```yaml -apiVersion: scaffolder.backstage.io/v1beta3 kind: Template metadata: - name: quarkus-web-template - title: Quarkus Hello world - description: Create a simple microservice using Quarkus - tags: - - java - - quarkus + name: simple-gitea-project + title: Create a gitea repository + description: Create a gitea repository spec: - owner: quarkus + owner: guests type: service + parameters: - - title: Git repository Information + - title: Choose a location required: - repoUrl properties: @@ -58,42 +65,33 @@ spec: type: string ui:field: RepoUrlPicker ui:options: + allowedOwners: + - qteam + - qshift allowedHosts: - - gitea.<YOUR_DOMAIN>:<PORT> - - localhost:<PORT> + - gitea.localtest.me:3333 + + repoVisibility: + title: Visibility of the repository + type: string + default: 'public' + enum: + - 'public' + - 'private' + enumNames: + - 'public' + - 'private' steps: - - id: template - name: Generating component - action: fetch:template - input: - url: ./skeleton - values: - name: ${{ parameters.name }} - + ... - id: publish name: Publishing to a gitea git repository action: publish:gitea input: - description: This is ${{ parameters.component_id }} + description: This is ${{ parameters.repoUrl | parseRepoUrl | pick('repo') }} + repoVisibility: ${{ parameters.repoVisibility }} repoUrl: ${{ parameters.repoUrl }} defaultBranch: main - - - id: register - if: ${{ parameters.dryRun !== true }} - name: Register - action: catalog:register - input: - repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }} - catalogInfoPath: 'main/catalog-info.yaml' - - output: - links: - - title: Source Code Repository - url: ${{ steps.publish.output.remoteUrl }} - - title: Open Component in catalog - icon: catalog - entityRef: ${{ steps.register.output.entityRef }} ``` Access the newly gitea repository created using the `repoContentsUrl` ;-) diff --git a/plugins/scaffolder-backend-module-gitea/api-report.md b/plugins/scaffolder-backend-module-gitea/api-report.md index 373cc6258c..b5e20b7b13 100644 --- a/plugins/scaffolder-backend-module-gitea/api-report.md +++ b/plugins/scaffolder-backend-module-gitea/api-report.md @@ -18,6 +18,7 @@ export function createPublishGiteaAction(options: { repoUrl: string; description: string; defaultBranch?: string | undefined; + repoVisibility?: 'private' | 'public' | undefined; gitCommitMessage?: string | undefined; gitAuthorName?: string | undefined; gitAuthorEmail?: string | undefined; diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index 1751a1a6ce..77bc3cd26e 100644 --- a/plugins/scaffolder-backend-module-gitea/package.json +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gitea", - "version": "0.1.7", + "version": "0.1.8-next.1", "description": "The gitea module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.examples.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.examples.ts index ccf0f4ddf6..dd4d038ead 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.examples.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.examples.ts @@ -50,6 +50,23 @@ export const examples: TemplateExample[] = [ ], }), }, + { + description: 'Initializes a private Gitea repository ', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:gitea', + name: 'Publish to Gitea', + input: { + repoUrl: 'gitea.com?repo=repo&owner=owner', + defaultBranch: 'main', + repoVisibility: 'private', + }, + }, + ], + }), + }, { description: 'Initializes a Gitea repository with a default Branch, if not set defaults to main', @@ -150,6 +167,7 @@ export const examples: TemplateExample[] = [ gitAuthorName: 'John Doe', gitAuthorEmail: 'johndoe@email.com', sourcePath: 'repository/', + repoVisibility: 'public', }, }, ], diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts index 8675b1ff97..8a72265df0 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts @@ -53,6 +53,13 @@ describe('publish:gitea', () => { description, }, }); + const mockContextWithPublicRepoVisibility = createMockActionContext({ + input: { + repoUrl: 'gitea.com?repo=repo&owner=owner', + description, + private: false, + }, + }); const server = setupServer(); setupRequestMockHandlers(server); @@ -111,6 +118,7 @@ describe('publish:gitea', () => { ); expect(req.body).toEqual({ name: 'repo', + private: false, description, }); return res( @@ -148,6 +156,71 @@ describe('publish:gitea', () => { ); }); + it('should create a Gitea repository where visibility is public', async () => { + server.use( + rest.get('https://gitea.com/api/v1/orgs/org1', (_req, res, ctx) => { + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json({ + id: 1, + name: 'org1', + visibility: 'public', + repo_admin_change_team_access: false, + username: 'org1', + }), + ); + }), + rest.get( + 'https://gitea.com/org1/repo/src/branch/main', + (_req, res, ctx) => { + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json({}), + ); + }, + ), + rest.post('https://gitea.com/api/v1/orgs/org1/repos', (req, res, ctx) => { + // Basic auth must match the user and password defined part of the config + expect(req.headers.get('Authorization')).toBe( + 'basic Z2l0ZWFfdXNlcjpnaXRlYV9wYXNzd29yZA==', + ); + expect(req.body).toEqual({ + name: 'repo', + private: false, + description, + }); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json({}), + ); + }), + ); + + await action.handler({ + ...mockContextWithPublicRepoVisibility, + input: { + ...mockContextWithPublicRepoVisibility.input, + repoUrl: 'gitea.com?repo=repo&owner=org1', + }, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContextWithPublicRepoVisibility.workspacePath, + remoteUrl: 'https://gitea.com/org1/repo.git', + defaultBranch: 'main', + auth: { username: 'gitea_user', password: 'gitea_password' }, + logger: mockContextWithPublicRepoVisibility.logger, + commitMessage: expect.stringContaining('initial commit\n\nChange-Id:'), + gitAuthorInfo: { + email: undefined, + name: undefined, + }, + }); + }); + afterEach(() => { jest.resetAllMocks(); }); diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index 089f9b2669..6b29ff2a4c 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -81,7 +81,7 @@ const checkGiteaOrg = async ( ); } catch (e) { throw new Error( - `Unable to get the Organization: ${owner}; Error cause: ${e.cause.message}`, + `Unable to get the Organization: ${owner}; Error cause: ${e.message}, code: ${e.cause.code}`, ); } if (response.status !== 200) { @@ -96,10 +96,11 @@ const createGiteaProject = async ( options: { projectName: string; owner?: string; + repoVisibility?: string; description: string; }, ): Promise<void> => { - const { projectName, description, owner } = options; + const { projectName, description, owner, repoVisibility } = options; /* Several options exist to create a repository using either the user or organisation @@ -112,12 +113,23 @@ const createGiteaProject = async ( This is the default scenario that we support currently */ let response: Response; + let isPrivate: boolean; + + if (repoVisibility === 'private') { + isPrivate = true; + } else if (repoVisibility === 'public') { + isPrivate = false; + } else { + // Provide a default value if repoVisibility is neither "private" nor "public" + isPrivate = false; + } const postOptions: RequestInit = { method: 'POST', body: JSON.stringify({ name: projectName, description, + private: isPrivate, }), headers: { ...getGiteaRequestOptions(config).headers, @@ -202,6 +214,7 @@ export function createPublishGiteaAction(options: { repoUrl: string; description: string; defaultBranch?: string; + repoVisibility?: 'private' | 'public'; gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; @@ -229,6 +242,12 @@ export function createPublishGiteaAction(options: { type: 'string', description: `Sets the default branch on the repository. The default value is 'main'`, }, + repoVisibility: { + title: 'Repository Visibility', + description: `Sets the visibility of the repository. The default value is 'public'.`, + type: 'string', + enum: ['private', 'public'], + }, gitCommitMessage: { title: 'Git Commit Message', type: 'string', @@ -274,6 +293,7 @@ export function createPublishGiteaAction(options: { repoUrl, description, defaultBranch = 'main', + repoVisibility = 'public', gitAuthorName, gitAuthorEmail, gitCommitMessage = 'initial commit', @@ -301,6 +321,7 @@ export function createPublishGiteaAction(options: { await createGiteaProject(integrationConfig.config, { description, + repoVisibility, owner: owner, projectName: repo, }); diff --git a/plugins/scaffolder-backend-module-github/CHANGELOG.md b/plugins/scaffolder-backend-module-github/CHANGELOG.md index 637cd5cb75..bcef59a58c 100644 --- a/plugins/scaffolder-backend-module-github/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-github/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-scaffolder-backend-module-github +## 0.2.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.2.8-next.0 + +### Patch Changes + +- 5d99272: Update local development dependencies. +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + ## 0.2.7 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-github/package.json b/plugins/scaffolder-backend-module-github/package.json index d1bec25994..8491b5877a 100644 --- a/plugins/scaffolder-backend-module-github/package.json +++ b/plugins/scaffolder-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-github", - "version": "0.2.7", + "version": "0.2.8-next.1", "description": "The github module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md index 133703847a..58f4cfdfb0 100644 --- a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-scaffolder-backend-module-gitlab +## 0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.3.4-next.0 + +### Patch Changes + +- ffc73ec: Add examples for `gitlab:repo:push` scaffolder action & improve related tests +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + ## 0.3.3 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index 32a2c64860..941dcb5aef 100644 --- a/plugins/scaffolder-backend-module-gitlab/package.json +++ b/plugins/scaffolder-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gitlab", - "version": "0.3.3", + "version": "0.3.4-next.1", "backstage": { "role": "backend-plugin-module" }, diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectVariableAction.examples.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectVariableAction.examples.test.ts new file mode 100644 index 0000000000..d1c37ccd9d --- /dev/null +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectVariableAction.examples.test.ts @@ -0,0 +1,236 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createGitlabProjectVariableAction } from './createGitlabProjectVariableAction'; +import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; +import { ScmIntegrations } from '@backstage/integration'; +import { ConfigReader } from '@backstage/config'; +import yaml from 'yaml'; +import { examples } from './createGitlabProjectVariableAction.examples'; + +const mockGitlabClient = { + ProjectVariables: { + create: jest.fn(), + }, +}; +jest.mock('@gitbeaker/node', () => ({ + Gitlab: class { + constructor() { + return mockGitlabClient; + } + }, +})); + +describe('gitlab:projectVariableAction: create examples', () => { + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + { + host: 'hosted.gitlab.com', + apiBaseUrl: 'https://api.hosted.gitlab.com', + }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + const action = createGitlabProjectVariableAction({ integrations }); + const mockContext = createMockActionContext({ + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: '123', + key: 'MY_VARIABLE', + value: 'my_value', + variableType: 'env_var', + }, + }); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it(`Should ${examples[0].description}`, async () => { + mockGitlabClient.ProjectVariables.create.mockResolvedValue({ + token: 'TOKEN', + }); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[0].example).steps[0].input, + }); + + expect(mockGitlabClient.ProjectVariables.create).toHaveBeenCalledWith( + '123', + { + key: 'MY_VARIABLE', + value: 'my_value', + variable_type: 'env_var', + environment_scope: '*', + masked: false, + protected: false, + raw: false, + }, + ); + }); + it(`Should ${examples[1].description}`, async () => { + mockGitlabClient.ProjectVariables.create.mockResolvedValue({ + token: 'TOKEN', + }); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[1].example).steps[0].input, + }); + + expect(mockGitlabClient.ProjectVariables.create).toHaveBeenCalledWith( + '123', + { + key: 'MY_VARIABLE', + value: 'my-file-content', + protected: false, + masked: false, + raw: false, + environment_scope: '*', + variable_type: 'file', + }, + ); + }); + + it(`Should ${examples[2].description}`, async () => { + mockGitlabClient.ProjectVariables.create.mockResolvedValue({ + token: 'TOKEN', + }); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[2].example).steps[0].input, + }); + + expect(mockGitlabClient.ProjectVariables.create).toHaveBeenCalledWith( + '456', + { + key: 'MY_VARIABLE', + value: 'my_value', + masked: false, + raw: false, + environment_scope: '*', + variable_type: 'env_var', + protected: true, + }, + ); + }); + + it(`Should ${examples[3].description}`, async () => { + mockGitlabClient.ProjectVariables.create.mockResolvedValue({ + token: 'TOKEN', + }); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[3].example).steps[0].input, + }); + + expect(mockGitlabClient.ProjectVariables.create).toHaveBeenCalledWith( + '789', + { + key: 'DB_PASSWORD', + value: 'password123', + protected: false, + raw: false, + environment_scope: '*', + variable_type: 'env_var', + masked: true, + }, + ); + }); + + it(`Should ${examples[4].description}`, async () => { + mockGitlabClient.ProjectVariables.create.mockResolvedValue({ + token: 'TOKEN', + }); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[4].example).steps[0].input, + }); + + expect(mockGitlabClient.ProjectVariables.create).toHaveBeenCalledWith( + '123', + { + key: 'MY_VARIABLE', + value: 'my_value', + protected: false, + environment_scope: '*', + variable_type: 'env_var', + masked: false, + raw: true, + }, + ); + }); + + it(`Should ${examples[5].description}`, async () => { + mockGitlabClient.ProjectVariables.create.mockResolvedValue({ + token: 'TOKEN', + }); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[5].example).steps[0].input, + }); + + expect(mockGitlabClient.ProjectVariables.create).toHaveBeenCalledWith( + '123', + { + key: 'MY_VARIABLE', + value: 'my_value', + protected: false, + variable_type: 'env_var', + masked: false, + raw: false, + environment_scope: 'production', + }, + ); + }); + + it(`Should ${examples[6].description}`, async () => { + mockGitlabClient.ProjectVariables.create.mockResolvedValue({ + token: 'TOKEN', + }); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[6].example).steps[0].input, + }); + + expect(mockGitlabClient.ProjectVariables.create).toHaveBeenCalledWith( + '123', + { + key: 'MY_VARIABLE', + value: 'my_value', + protected: false, + variable_type: 'env_var', + masked: false, + raw: false, + environment_scope: '*', + }, + ); + }); +}); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectVariableAction.examples.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectVariableAction.examples.ts new file mode 100644 index 0000000000..81d526f7c8 --- /dev/null +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectVariableAction.examples.ts @@ -0,0 +1,160 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Creating a GitLab project variable of type env_var', + example: yaml.stringify({ + steps: [ + { + id: 'createVariable', + action: 'gitlab:createGitlabProjectVariableAction', + name: 'Create GitLab Project Variable', + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: '123', + key: 'MY_VARIABLE', + value: 'my_value', + variableType: 'env_var', + }, + }, + ], + }), + }, + { + description: 'Creating a GitLab project variable of type file', + example: yaml.stringify({ + steps: [ + { + id: 'createVariable', + action: 'gitlab:createGitlabProjectVariableAction', + name: 'Create GitLab Project Variable', + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: '123', + key: 'MY_VARIABLE', + value: 'my-file-content', + variableType: 'file', + }, + }, + ], + }), + }, + { + description: 'Create a GitLab project variable that is protected.', + example: yaml.stringify({ + steps: [ + { + id: 'createVariable', + action: 'gitlab:createGitlabProjectVariableAction', + name: 'Create GitLab Project Variable', + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: '456', + key: 'MY_VARIABLE', + value: 'my_value', + variableType: 'env_var', + variableProtected: true, + }, + }, + ], + }), + }, + { + description: 'Create a GitLab project variable with masked flag as true', + example: yaml.stringify({ + steps: [ + { + id: 'createVariable', + action: 'gitlab:createGitlabProjectVariableAction', + name: 'Create GitLab Project Variable', + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: '789', + key: 'DB_PASSWORD', + value: 'password123', + variableType: 'env_var', + masked: true, + }, + }, + ], + }), + }, + { + description: 'Create a GitLab project variable that is expandable.', + example: yaml.stringify({ + steps: [ + { + id: 'createVariable', + action: 'gitlab:projectVariable:create', + name: 'Create GitLab Project Variable', + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: '123', + key: 'MY_VARIABLE', + value: 'my_value', + variableType: 'env_var', + raw: true, + }, + }, + ], + }), + }, + { + description: + 'Create a GitLab project variable with a specific environment scope.', + example: yaml.stringify({ + steps: [ + { + id: 'createVariable', + action: 'gitlab:projectVariable:create', + name: 'Create GitLab Project Variable', + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: '123', + key: 'MY_VARIABLE', + value: 'my_value', + variableType: 'env_var', + environmentScope: 'production', + }, + }, + ], + }), + }, + { + description: + 'Create a GitLab project variable with a wildcard environment scope.', + example: yaml.stringify({ + steps: [ + { + id: 'createVariable', + action: 'gitlab:projectVariable:create', + name: 'Create GitLab Project Variable', + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: '123', + key: 'MY_VARIABLE', + value: 'my_value', + variableType: 'env_var', + environmentScope: '*', + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectVariableAction.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectVariableAction.ts index a154c16662..e09a074701 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectVariableAction.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectVariableAction.ts @@ -20,6 +20,7 @@ import { Gitlab } from '@gitbeaker/node'; import { getToken } from '../util'; import commonGitlabConfig from '../commonGitlabConfig'; import { z } from 'zod'; +import { examples } from './createGitlabProjectVariableAction.examples'; /** * Creates a `gitlab:projectVariable:create` Scaffolder action. @@ -33,6 +34,7 @@ export const createGitlabProjectVariableAction = (options: { const { integrations } = options; return createTemplateAction({ id: 'gitlab:projectVariable:create', + examples, schema: { input: commonGitlabConfig.merge( z.object({ @@ -85,7 +87,6 @@ export const createGitlabProjectVariableAction = (options: { host: integrationConfig.config.baseUrl, token: token, }); - await api.ProjectVariables.create(projectId, { key: key, value: value, diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index 607d3d11f7..4553938eab 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.4.35-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.4.35-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/types@1.1.1 + ## 0.4.34 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index 9d54373111..a523ed9a8d 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-rails", - "version": "0.4.34", + "version": "0.4.35-next.1", "description": "A module for the scaffolder backend that lets you template projects using Rails", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md index 54c6bb06f2..d3685153d5 100644 --- a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-scaffolder-backend-module-sentry +## 0.1.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + ## 0.1.25 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index f57c38a003..bd7555a8c2 100644 --- a/plugins/scaffolder-backend-module-sentry/package.json +++ b/plugins/scaffolder-backend-module-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-sentry", - "version": "0.1.25", + "version": "0.1.26-next.1", "backstage": { "role": "backend-plugin-module" }, diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index f0f737aa82..96b7222d0f 100644 --- a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-scaffolder-backend-module-yeoman +## 0.3.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/plugin-scaffolder-node-test-utils@0.1.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node-test-utils@0.1.4-next.0 + ## 0.3.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index f75fba4195..c0f0c85f72 100644 --- a/plugins/scaffolder-backend-module-yeoman/package.json +++ b/plugins/scaffolder-backend-module-yeoman/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-yeoman", - "version": "0.3.0", + "version": "0.3.1-next.1", "backstage": { "role": "backend-plugin-module" }, diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 12ca699696..0ed790e3be 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,60 @@ # @backstage/plugin-scaffolder-backend +## 1.22.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.5.2-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.16-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.2.8-next.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.3.4-next.1 + - @backstage/plugin-scaffolder-backend-module-azure@0.1.10-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.2.8-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.8-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.8-next.1 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.1.10-next.1 + - @backstage/plugin-scaffolder-backend-module-gitea@0.1.8-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 1.22.5-next.0 + +### Patch Changes + +- 025641b: Fix issue with the log format not being respected when logging from actions +- 025641b: Redact `meta` fields too with the logger +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-github@0.2.8-next.0 + - @backstage/plugin-scaffolder-backend-module-gitea@0.1.8-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.3.4-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.1.8-next.0 + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.16-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-scaffolder-common@1.5.2-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + - @backstage/plugin-scaffolder-backend-module-azure@0.1.10-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.2.8-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.1.8-next.0 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.1.10-next.0 + ## 1.22.4 ### Patch Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index b967580154..338dc290e1 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "1.22.4", + "version": "1.22.5-next.1", "description": "The Backstage backend plugin that helps you create new things", "backstage": { "role": "backend-plugin" @@ -95,6 +95,7 @@ "prom-client": "^15.0.0", "uuid": "^9.0.0", "winston": "^3.2.1", + "winston-transport": "^4.7.0", "yaml": "^2.0.0", "zen-observable": "^0.10.0", "zod": "^3.22.4" diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts index 81897a96f8..5d24ac7cc7 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts @@ -55,7 +55,7 @@ import { actionExecutePermission } from '@backstage/plugin-scaffolder-common/alp import { TaskRecovery } from '@backstage/plugin-scaffolder-common'; import { PermissionsService } from '@backstage/backend-plugin-api'; import { loggerToWinstonLogger } from '@backstage/backend-common'; -import { WinstonLogger } from './logger'; +import { BackstageLoggerTransport, WinstonLogger } from './logger'; type NunjucksWorkflowRunnerOptions = { workingDirectory: string; @@ -98,9 +98,11 @@ const isValidTaskSpec = (taskSpec: TaskSpec): taskSpec is TaskSpecV1beta3 => { const createStepLogger = ({ task, step, + rootLogger, }: { task: TaskContext; step: TaskStep; + rootLogger: winston.Logger; }) => { const stepLogStream = new PassThrough(); stepLogStream.on('data', async data => { @@ -117,8 +119,8 @@ const createStepLogger = ({ winston.format.simple(), ), transports: [ - new winston.transports.Console(), new winston.transports.Stream({ stream: stepLogStream }), + new BackstageLoggerTransport(rootLogger), ], }); @@ -262,7 +264,11 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { const action: TemplateAction<JsonObject> = this.options.actionRegistry.get(step.action); - const { taskLogger, streamLogger } = createStepLogger({ task, step }); + const { taskLogger, streamLogger } = createStepLogger({ + task, + step, + rootLogger: this.options.logger, + }); if (task.isDryRun) { const redactedSecrets = Object.fromEntries( diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/logger.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/logger.ts index b96b747077..064eb69349 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/logger.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/logger.ts @@ -19,13 +19,8 @@ import { } from '@backstage/backend-plugin-api'; import { JsonObject } from '@backstage/types'; import { Format, TransformableInfo } from 'logform'; -import { - Logger, - format, - createLogger, - transports, - transport as Transport, -} from 'winston'; +import Transport, { TransportStreamOptions } from 'winston-transport'; +import { Logger, format, createLogger, transports } from 'winston'; /** * Escapes a given string to be used inside a RegExp. @@ -43,6 +38,42 @@ interface WinstonLoggerOptions { transports: Transport[]; } +// This is a workaround for being able to preserve the log format of the root logger. +// Will revisit all of this implementation once we can break the router to use only `LoggerService`. +export class BackstageLoggerTransport extends Transport { + constructor( + private readonly backstageLogger: LoggerService, + opts?: TransportStreamOptions, + ) { + super(opts); + } + + log(info: unknown, callback: VoidFunction) { + if (typeof info !== 'object' || info === null) { + callback(); + return; + } + const { level, message, ...meta } = info as JsonObject; + switch (level) { + case 'error': + this.backstageLogger.error(String(message), meta); + break; + case 'warn': + this.backstageLogger.warn(String(message), meta); + break; + case 'info': + this.backstageLogger.info(String(message), meta); + break; + case 'debug': + this.backstageLogger.debug(String(message), meta); + break; + default: + this.backstageLogger.info(String(message), meta); + } + callback(); + } +} + export class WinstonLogger implements RootLoggerService { #winston: Logger; #addRedactions?: (redactions: Iterable<string>) => void; @@ -76,16 +107,20 @@ export class WinstonLogger implements RootLoggerService { let redactionPattern: RegExp | undefined = undefined; + const replace = (obj: TransformableInfo) => { + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + if (typeof obj[key] === 'object') { + obj[key] = replace(obj[key] as TransformableInfo); + } else if (typeof obj[key] === 'string') { + obj[key] = obj[key]?.replace(redactionPattern, '[REDACTED]'); + } + } + } + return obj; + }; return { - format: format(info => { - if (redactionPattern && typeof info.message === 'string') { - info.message = info.message.replace(redactionPattern, '[REDACTED]'); - } - if (redactionPattern && typeof info.stack === 'string') { - info.stack = info.stack.replace(redactionPattern, '[REDACTED]'); - } - return info; - })(), + format: format(replace)(), add(newRedactions) { let added = 0; for (const redactionToTrim of newRedactions) { diff --git a/plugins/scaffolder-common/CHANGELOG.md b/plugins/scaffolder-common/CHANGELOG.md index ed58b28338..4a9e1b870b 100644 --- a/plugins/scaffolder-common/CHANGELOG.md +++ b/plugins/scaffolder-common/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-scaffolder-common +## 1.5.2-next.1 + +### Patch Changes + +- 9156654: Capturing more event clicks for scaffolder + +## 1.5.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + ## 1.5.1 ### Patch Changes diff --git a/plugins/scaffolder-common/package.json b/plugins/scaffolder-common/package.json index 9f99434371..614bd16a25 100644 --- a/plugins/scaffolder-common/package.json +++ b/plugins/scaffolder-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-common", - "version": "1.5.1", + "version": "1.5.2-next.1", "description": "Common functionalities for the scaffolder, to be shared between scaffolder and scaffolder-backend plugin", "backstage": { "role": "common-library" diff --git a/plugins/scaffolder-common/src/Template.v1beta3.schema.json b/plugins/scaffolder-common/src/Template.v1beta3.schema.json index 99b0e0b7cb..4e34545d6d 100644 --- a/plugins/scaffolder-common/src/Template.v1beta3.schema.json +++ b/plugins/scaffolder-common/src/Template.v1beta3.schema.json @@ -141,19 +141,25 @@ }, "presentation": { "type": "object", - "description": "A way to redefine the labels for actionable buttons.", + "description": "A way to redefine the presentation of the scaffolder.", "properties": { - "backButtonText": { - "type": "string", - "description": "A button which return the user to one step back." - }, - "createButtonText": { - "type": "string", - "description": "A button which start the execution of the template." - }, - "reviewButtonText": { - "type": "string", - "description": "A button which open the review step to verify the input prior to start the execution." + "buttonLabels": { + "type": "object", + "description": "A way to redefine the labels for actionable buttons.", + "properties": { + "backButtonText": { + "type": "string", + "description": "A button which return the user to one step back." + }, + "createButtonText": { + "type": "string", + "description": "A button which start the execution of the template." + }, + "reviewButtonText": { + "type": "string", + "description": "A button which open the review step to verify the input prior to start the execution." + } + } } } }, diff --git a/plugins/scaffolder-node-test-utils/CHANGELOG.md b/plugins/scaffolder-node-test-utils/CHANGELOG.md index 8033908b54..5914c8fdee 100644 --- a/plugins/scaffolder-node-test-utils/CHANGELOG.md +++ b/plugins/scaffolder-node-test-utils/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-scaffolder-node-test-utils +## 0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.4-next.1 + - @backstage/backend-test-utils@0.3.8-next.1 + +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-test-utils@0.3.8-next.0 + - @backstage/plugin-scaffolder-node@0.4.4-next.0 + - @backstage/types@1.1.1 + ## 0.1.3 ### Patch Changes diff --git a/plugins/scaffolder-node-test-utils/package.json b/plugins/scaffolder-node-test-utils/package.json index 7b8379642d..99adae5ab7 100644 --- a/plugins/scaffolder-node-test-utils/package.json +++ b/plugins/scaffolder-node-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node-test-utils", - "version": "0.1.3", + "version": "0.1.4-next.1", "backstage": { "role": "node-library" }, diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index ef7ff6f7f4..f1739c59e4 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-scaffolder-node +## 0.4.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.5.2-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.4.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/plugin-scaffolder-common@1.5.2-next.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/types@1.1.1 + ## 0.4.3 ### Patch Changes diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index 0c063cb0df..f0776e6491 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node", - "version": "0.4.3", + "version": "0.4.4-next.1", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "node-library" @@ -33,7 +33,6 @@ } }, "files": [ - "alpha", "dist" ], "scripts": { diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index 569b1215bd..9c3a25638d 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,34 @@ # @backstage/plugin-scaffolder-react +## 1.8.5-next.1 + +### Patch Changes + +- 9156654: Capturing more event clicks for scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.5.2-next.1 + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + +## 1.8.5-next.0 + +### Patch Changes + +- 0040ec2: Updated dependency `@rjsf/utils` to `5.18.2`. + Updated dependency `@rjsf/core` to `5.18.2`. + Updated dependency `@rjsf/material-ui` to `5.18.2`. + Updated dependency `@rjsf/validator-ajv8` to `5.18.2`. +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-scaffolder-common@1.5.2-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + ## 1.8.4 ### Patch Changes diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index 06fb9fe5a2..b7b7b20014 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-react", - "version": "1.8.4", + "version": "1.8.5-next.1", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", "backstage": { "role": "web-library" diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx index 62effbd14a..5fbda65957 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx @@ -35,9 +35,8 @@ import { type FormValidation, } from './createAsyncValidators'; import { ReviewState, type ReviewStateProps } from '../ReviewState'; -import { useTemplateSchema } from '../../hooks/useTemplateSchema'; +import { useTemplateSchema, useFormDataFromQuery } from '../../hooks'; import validator from '@rjsf/validator-ajv8'; -import { useFormDataFromQuery } from '../../hooks'; import { useTransformSchemaToProps } from '../../hooks/useTransformSchemaToProps'; import { hasErrors } from './utils'; import * as FieldOverrides from './FieldOverrides'; @@ -115,6 +114,13 @@ export const Stepper = (stepperProps: StepperProps) => { const [errors, setErrors] = useState<undefined | FormValidation>(); const styles = useStyles(); + const backLabel = + presentation?.buttonLabels?.backButtonText ?? backButtonText; + const createLabel = + presentation?.buttonLabels?.createButtonText ?? createButtonText; + const reviewLabel = + presentation?.buttonLabels?.reviewButtonText ?? reviewButtonText; + const extensions = useMemo(() => { return Object.fromEntries( props.extensions.map(({ name, component }) => [name, component]), @@ -150,7 +156,8 @@ export const Stepper = (stepperProps: StepperProps) => { const handleCreate = useCallback(() => { props.onCreate(formState); - }, [props, formState]); + analytics.captureEvent('click', `${createLabel}`); + }, [props, formState, analytics, createLabel]); const currentStep = useTransformSchemaToProps(steps[activeStep], { layouts }); @@ -181,13 +188,6 @@ export const Stepper = (stepperProps: StepperProps) => { setFormState(current => ({ ...current, ...formData })); }; - const backLabel = - presentation?.buttonLabels?.backButtonText ?? backButtonText; - const createLabel = - presentation?.buttonLabels?.createButtonText ?? createButtonText; - const reviewLabel = - presentation?.buttonLabels?.reviewButtonText ?? reviewButtonText; - return ( <> {isValidating && <LinearProgress variant="indeterminate" />} @@ -214,7 +214,7 @@ export const Stepper = (stepperProps: StepperProps) => { ); })} <MuiStep> - <MuiStepLabel>Review</MuiStepLabel> + <MuiStepLabel>${reviewLabel}</MuiStepLabel> </MuiStep> </MuiStepper> <div className={styles.formWrapper}> @@ -274,7 +274,7 @@ export const Stepper = (stepperProps: StepperProps) => { className={styles.backButton} disabled={activeStep < 1} > - Back + {backLabel} </Button> <Button variant="contained" diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx index 4fb2ca0e4a..4d4583a7d7 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx @@ -16,7 +16,11 @@ import { RELATION_OWNED_BY } from '@backstage/catalog-model'; import { MarkdownContent, UserIcon } from '@backstage/core-components'; -import { IconComponent, useApp } from '@backstage/core-plugin-api'; +import { + IconComponent, + useAnalytics, + useApp, +} from '@backstage/core-plugin-api'; import { EntityRefLinks, getEntityRelations, @@ -32,7 +36,7 @@ import Button from '@material-ui/core/Button'; import Grid from '@material-ui/core/Grid'; import { makeStyles, Theme } from '@material-ui/core/styles'; import LanguageIcon from '@material-ui/icons/Language'; -import React from 'react'; +import React, { useCallback } from 'react'; import { CardHeader } from './CardHeader'; import { CardLink } from './CardLink'; @@ -92,8 +96,9 @@ export interface TemplateCardProps { * @alpha */ export const TemplateCard = (props: TemplateCardProps) => { - const { template } = props; + const { onSelected, template } = props; const styles = useStyles(); + const analytics = useAnalytics(); const ownedByRelations = getEntityRelations(template, RELATION_OWNED_BY); const app = useApp(); const iconResolver = (key?: string): IconComponent => @@ -103,6 +108,11 @@ export const TemplateCard = (props: TemplateCardProps) => { !!props.additionalLinks?.length || !!template.metadata.links?.length; const displayDefaultDivider = !hasTags && !hasLinks; + const handleChoose = useCallback(() => { + analytics.captureEvent('click', `Template has been opened`); + onSelected?.(template); + }, [analytics, onSelected, template]); + return ( <Card> <CardHeader template={template} /> @@ -190,7 +200,7 @@ export const TemplateCard = (props: TemplateCardProps) => { size="small" variant="outlined" color="primary" - onClick={() => props.onSelected?.(template)} + onClick={handleChoose} > Choose </Button> diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index a7be4be176..9a7ecd2e50 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,44 @@ # @backstage/plugin-scaffolder +## 1.19.4-next.1 + +### Patch Changes + +- 9156654: Capturing more event clicks for scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.5.2-next.1 + - @backstage/plugin-scaffolder-react@1.8.5-next.1 + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/core-compat-api@0.2.5-next.1 + +## 1.19.4-next.0 + +### Patch Changes + +- 0040ec2: Updated dependency `@rjsf/utils` to `5.18.2`. + Updated dependency `@rjsf/core` to `5.18.2`. + Updated dependency `@rjsf/material-ui` to `5.18.2`. + Updated dependency `@rjsf/validator-ajv8` to `5.18.2`. +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-scaffolder-react@1.8.5-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-scaffolder-common@1.5.2-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/integration@1.10.0 + - @backstage/integration-react@1.1.26 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-react@0.4.22 + ## 1.19.3 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index b8d0f21805..14c1d5b2de 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "1.19.3", + "version": "1.19.4-next.1", "description": "The Backstage plugin that helps you create new things", "backstage": { "role": "frontend-plugin" diff --git a/plugins/scaffolder/src/components/OngoingTask/ContextMenu.tsx b/plugins/scaffolder/src/components/OngoingTask/ContextMenu.tsx index 99d9235791..1dd6a48f64 100644 --- a/plugins/scaffolder/src/components/OngoingTask/ContextMenu.tsx +++ b/plugins/scaffolder/src/components/OngoingTask/ContextMenu.tsx @@ -28,7 +28,7 @@ import Toc from '@material-ui/icons/Toc'; import ControlPointIcon from '@material-ui/icons/ControlPoint'; import MoreVert from '@material-ui/icons/MoreVert'; import React, { useState } from 'react'; -import { useApi } from '@backstage/core-plugin-api'; +import { useAnalytics, useApi } from '@backstage/core-plugin-api'; import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react'; type ContextMenuProps = { @@ -61,10 +61,12 @@ export const ContextMenu = (props: ContextMenuProps) => { const pageTheme = getPageTheme({ themeId: 'website' }); const classes = useStyles({ fontColor: pageTheme.fontColor }); const scaffolderApi = useApi(scaffolderApiRef); + const analytics = useAnalytics(); const [anchorEl, setAnchorEl] = useState<HTMLButtonElement>(); const [{ status: cancelStatus }, { execute: cancel }] = useAsync(async () => { if (taskId) { + analytics.captureEvent('cancelled', 'Template has been cancelled'); await scaffolderApi.cancelTask(taskId); } }); diff --git a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx index 3e7a3b8f52..f50b76e14a 100644 --- a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx +++ b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx @@ -26,7 +26,7 @@ import { useTaskEventStream, } from '@backstage/plugin-scaffolder-react'; import { selectedTemplateRouteRef } from '../../routes'; -import { useApi, useRouteRef } from '@backstage/core-plugin-api'; +import { useAnalytics, useApi, useRouteRef } from '@backstage/core-plugin-api'; import qs from 'qs'; import { ContextMenu } from './ContextMenu'; import { @@ -66,6 +66,7 @@ export const OngoingTask = (props: { const { taskId } = useParams(); const templateRouteRef = useRouteRef(selectedTemplateRouteRef); const navigate = useNavigate(); + const analytics = useAnalytics(); const scaffolderApi = useApi(scaffolderApiRef); const taskStream = useTaskEventStream(taskId!); const classes = useStyles(); @@ -113,6 +114,8 @@ export const OngoingTask = (props: { return; } + analytics.captureEvent('click', `Task has been started over`); + navigate({ pathname: templateRouteRef({ namespace, @@ -121,6 +124,7 @@ export const OngoingTask = (props: { search: `?${qs.stringify({ formData: JSON.stringify(formData) })}`, }); }, [ + analytics, navigate, taskStream.task?.spec.parameters, taskStream.task?.spec.templateInfo?.entity?.metadata, @@ -130,6 +134,7 @@ export const OngoingTask = (props: { const [{ status: cancelStatus }, { execute: triggerCancel }] = useAsync( async () => { if (taskId) { + analytics.captureEvent('cancelled', 'Template has been cancelled'); await scaffolderApi.cancelTask(taskId); } }, diff --git a/plugins/search-backend-module-catalog/CHANGELOG.md b/plugins/search-backend-module-catalog/CHANGELOG.md index 1d985231b2..fb9977e0ea 100644 --- a/plugins/search-backend-module-catalog/CHANGELOG.md +++ b/plugins/search-backend-module-catalog/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-search-backend-module-catalog +## 0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.23-next.0 + +### Patch Changes + +- d5fff66: Fix wiring of the module exported at the `/alpha` path, which was causing authentication failures. +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-search-common@1.2.11 + ## 0.1.22 ### Patch Changes diff --git a/plugins/search-backend-module-catalog/package.json b/plugins/search-backend-module-catalog/package.json index 6aebb192d9..ad03071eaf 100644 --- a/plugins/search-backend-module-catalog/package.json +++ b/plugins/search-backend-module-catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-catalog", - "version": "0.1.22", + "version": "0.1.24-next.1", "description": "A module for the search backend that exports catalog modules", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/search-backend-module-elasticsearch/CHANGELOG.md b/plugins/search-backend-module-elasticsearch/CHANGELOG.md index 7ca82061d1..1af0204403 100644 --- a/plugins/search-backend-module-elasticsearch/CHANGELOG.md +++ b/plugins/search-backend-module-elasticsearch/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-search-backend-module-elasticsearch +## 1.4.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 1.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-search-common@1.2.11 + ## 1.4.0 ### Minor Changes diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index 1d34b60f59..85489b4043 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-elasticsearch", - "version": "1.4.0", + "version": "1.4.1-next.1", "description": "A module for the search backend that implements search using ElasticSearch", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/search-backend-module-explore/CHANGELOG.md b/plugins/search-backend-module-explore/CHANGELOG.md index 5a3adbbbf7..ce6499ab4e 100644 --- a/plugins/search-backend-module-explore/CHANGELOG.md +++ b/plugins/search-backend-module-explore/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-search-backend-module-explore +## 0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.23-next.0 + +### Patch Changes + +- ca6e2e0: Migrate search collator to use the new auth services. +- 5d99272: Update README.md to point to explore plugin in community-plugins repository. +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-search-common@1.2.11 + ## 0.1.21 ### Patch Changes diff --git a/plugins/search-backend-module-explore/package.json b/plugins/search-backend-module-explore/package.json index 4741e2fde2..d181d70cbf 100644 --- a/plugins/search-backend-module-explore/package.json +++ b/plugins/search-backend-module-explore/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-explore", - "version": "0.1.21", + "version": "0.1.24-next.1", "description": "A module for the search backend that exports explore modules", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/search-backend-module-explore/src/alpha.ts b/plugins/search-backend-module-explore/src/alpha.ts index 5a990b19f6..21c5901885 100644 --- a/plugins/search-backend-module-explore/src/alpha.ts +++ b/plugins/search-backend-module-explore/src/alpha.ts @@ -44,6 +44,7 @@ export default createBackendModule({ discovery: coreServices.discovery, scheduler: coreServices.scheduler, tokenManager: coreServices.tokenManager, + auth: coreServices.auth, indexRegistry: searchIndexRegistryExtensionPoint, }, async init({ @@ -51,8 +52,9 @@ export default createBackendModule({ logger, discovery, scheduler, - indexRegistry, tokenManager, + auth, + indexRegistry, }) { const defaultSchedule = { frequency: { minutes: 10 }, @@ -71,6 +73,7 @@ export default createBackendModule({ factory: ToolDocumentCollatorFactory.fromConfig(config, { discovery, logger, + auth, tokenManager, }), }); diff --git a/plugins/search-backend-module-pg/CHANGELOG.md b/plugins/search-backend-module-pg/CHANGELOG.md index c085ee456d..37386a7fd1 100644 --- a/plugins/search-backend-module-pg/CHANGELOG.md +++ b/plugins/search-backend-module-pg/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-search-backend-module-pg +## 0.5.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.5.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-search-common@1.2.11 + ## 0.5.26 ### Patch Changes diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index 505d2408f6..3aec50e503 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-pg", - "version": "0.5.26", + "version": "0.5.27-next.1", "description": "A module for the search backend that implements search using PostgreSQL", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md index 604d458bc2..c8faaf7609 100644 --- a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md +++ b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-search-backend-module-stack-overflow-collator +## 0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-search-common@1.2.11 + ## 0.1.10 ### Patch Changes diff --git a/plugins/search-backend-module-stack-overflow-collator/package.json b/plugins/search-backend-module-stack-overflow-collator/package.json index f1c10b414f..3c76ed164b 100644 --- a/plugins/search-backend-module-stack-overflow-collator/package.json +++ b/plugins/search-backend-module-stack-overflow-collator/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-stack-overflow-collator", - "version": "0.1.10", + "version": "0.1.11-next.1", "description": "A module for the search backend that exports stack overflow modules", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/search-backend-module-techdocs/CHANGELOG.md b/plugins/search-backend-module-techdocs/CHANGELOG.md index 9b909f66f9..e79c98ab5a 100644 --- a/plugins/search-backend-module-techdocs/CHANGELOG.md +++ b/plugins/search-backend-module-techdocs/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-search-backend-module-techdocs +## 0.1.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/plugin-techdocs-node@1.12.4-next.1 + - @backstage/plugin-catalog-node@1.11.2-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-catalog-node@1.11.2-next.0 + - @backstage/plugin-techdocs-node@1.12.4-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-search-common@1.2.11 + ## 0.1.22 ### Patch Changes diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index 57a880b052..09aa136f0e 100644 --- a/plugins/search-backend-module-techdocs/package.json +++ b/plugins/search-backend-module-techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-techdocs", - "version": "0.1.22", + "version": "0.1.23-next.1", "description": "A module for the search backend that exports techdocs modules", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/search-backend-node/CHANGELOG.md b/plugins/search-backend-node/CHANGELOG.md index 568c04889a..854996ba53 100644 --- a/plugins/search-backend-node/CHANGELOG.md +++ b/plugins/search-backend-node/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-search-backend-node +## 1.2.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-tasks@0.5.23-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 1.2.22-next.0 + +### Patch Changes + +- c6cb568: Add lifecycle monitoring for the search index registry +- Updated dependencies + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/backend-tasks@0.5.23-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-search-common@1.2.11 + ## 1.2.21 ### Patch Changes diff --git a/plugins/search-backend-node/api-report-alpha.md b/plugins/search-backend-node/api-report-alpha.md index c8e5cbb65d..8e35e70f65 100644 --- a/plugins/search-backend-node/api-report-alpha.md +++ b/plugins/search-backend-node/api-report-alpha.md @@ -34,6 +34,7 @@ export const searchIndexRegistryExtensionPoint: ExtensionPoint<SearchIndexRegist export interface SearchIndexService { getDocumentTypes(): Record<string, DocumentTypeInfo>; start(options: SearchIndexServiceStartOptions): Promise<void>; + stop(): Promise<void>; } // @alpha diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index c6444d0fef..c734681980 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-node", - "version": "1.2.21", + "version": "1.2.22-next.1", "description": "A library for Backstage backend plugins that want to interact with the search backend plugin", "backstage": { "role": "node-library" diff --git a/plugins/search-backend-node/src/alpha.ts b/plugins/search-backend-node/src/alpha.ts index 43bda67658..331f0ae304 100644 --- a/plugins/search-backend-node/src/alpha.ts +++ b/plugins/search-backend-node/src/alpha.ts @@ -22,11 +22,11 @@ import { LoggerService, } from '@backstage/backend-plugin-api'; import { DocumentTypeInfo } from '@backstage/plugin-search-common'; - import { IndexBuilder, RegisterCollatorParameters, RegisterDecoratorParameters, + Scheduler, SearchEngine, } from '@backstage/plugin-search-backend-node'; @@ -49,6 +49,11 @@ export interface SearchIndexService { * Starts indexing process */ start(options: SearchIndexServiceStartOptions): Promise<void>; + + /** + * Stops indexing process + */ + stop(): Promise<void>; /** * Returns an index types list. */ @@ -81,8 +86,9 @@ type DefaultSearchIndexServiceOptions = { * Reponsible for register the indexing task and start the schedule. */ class DefaultSearchIndexService implements SearchIndexService { - private logger: LoggerService; + private readonly logger: LoggerService; private indexBuilder: IndexBuilder | null = null; + private scheduler: Scheduler | null = null; private constructor(options: DefaultSearchIndexServiceOptions) { this.logger = options.logger; @@ -107,7 +113,15 @@ class DefaultSearchIndexService implements SearchIndexService { ); const { scheduler } = await this.indexBuilder?.build(); - scheduler.start(); + this.scheduler = scheduler; + this.scheduler!.start(); + } + + async stop(): Promise<void> { + if (this.scheduler) { + this.scheduler.stop(); + this.scheduler = null; + } } getDocumentTypes(): Record<string, DocumentTypeInfo> { diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index c611e41c7a..5dabd9f1a5 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-search-backend +## 1.5.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/repo-tools@0.8.1-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-permission-node@0.7.29-next.1 + - @backstage/plugin-search-backend-node@1.2.22-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + - @backstage/backend-openapi-utils@0.1.11-next.1 + +## 1.5.8-next.0 + +### Patch Changes + +- c6cb568: Add lifecycle monitoring for the search index registry +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.22-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/repo-tools@0.8.1-next.0 + - @backstage/backend-openapi-utils@0.1.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.13 + - @backstage/plugin-permission-node@0.7.29-next.0 + - @backstage/plugin-search-common@1.2.11 + ## 1.5.7 ### Patch Changes diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index d6f554e77e..c06b99ec92 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend", - "version": "1.5.7", + "version": "1.5.8-next.1", "description": "The Backstage backend plugin that provides your backstage app with search", "backstage": { "role": "backend-plugin" diff --git a/plugins/search-backend/src/alpha.ts b/plugins/search-backend/src/alpha.ts index 0ca5777102..a17b542903 100644 --- a/plugins/search-backend/src/alpha.ts +++ b/plugins/search-backend/src/alpha.ts @@ -98,6 +98,7 @@ export default createBackendPlugin({ auth: coreServices.auth, http: coreServices.httpRouter, httpAuth: coreServices.httpAuth, + lifecycle: coreServices.rootLifecycle, searchIndexService: searchIndexServiceRef, }, async init({ @@ -108,6 +109,7 @@ export default createBackendPlugin({ auth, http, httpAuth, + lifecycle, searchIndexService, }) { let searchEngine = searchEngineRegistry.getSearchEngine(); @@ -120,10 +122,16 @@ export default createBackendPlugin({ const collators = searchIndexRegistry.getCollators(); const decorators = searchIndexRegistry.getDecorators(); - await searchIndexService.start({ - searchEngine, - collators, - decorators, + lifecycle.addStartupHook(async () => { + await searchIndexService.start({ + searchEngine: searchEngine!, + collators, + decorators, + }); + }); + + lifecycle.addShutdownHook(async () => { + await searchIndexService.stop(); }); const router = await createRouter({ diff --git a/plugins/search-react/CHANGELOG.md b/plugins/search-react/CHANGELOG.md index f9f8c0bc30..c4e75fa41a 100644 --- a/plugins/search-react/CHANGELOG.md +++ b/plugins/search-react/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-search-react +## 1.7.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + +## 1.7.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-search-common@1.2.11 + ## 1.7.10 ### Patch Changes diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index 7fcb34801c..274deae1d7 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-react", - "version": "1.7.10", + "version": "1.7.11-next.1", "backstage": { "role": "web-library" }, diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 6846a947c4..cde6f55c14 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-search +## 1.4.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/plugin-search-react@1.7.11-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## 1.4.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-search-common@1.2.11 + - @backstage/plugin-search-react@1.7.11-next.0 + ## 1.4.10 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index 65539ce949..06f5ff47e7 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search", - "version": "1.4.10", + "version": "1.4.11-next.1", "description": "The Backstage plugin that provides your backstage app with search", "backstage": { "role": "frontend-plugin" diff --git a/plugins/search/src/components/SearchModal/SearchModal.tsx b/plugins/search/src/components/SearchModal/SearchModal.tsx index 814779fb45..07eec4438e 100644 --- a/plugins/search/src/components/SearchModal/SearchModal.tsx +++ b/plugins/search/src/components/SearchModal/SearchModal.tsx @@ -190,7 +190,8 @@ export const SearchModal = (props: SearchModalProps) => { paperFullWidth: classes.paperFullWidth, }} onClose={toggleModal} - aria-labelledby="search-modal-title" + aria-label="Search Modal" + aria-modal="true" fullWidth maxWidth="lg" open={open} diff --git a/plugins/signals-backend/CHANGELOG.md b/plugins/signals-backend/CHANGELOG.md index 4fd519b225..fcd34ead2e 100644 --- a/plugins/signals-backend/CHANGELOG.md +++ b/plugins/signals-backend/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-signals-backend +## 0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/plugin-signals-node@0.1.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.3.4-next.0 + - @backstage/plugin-signals-node@0.1.4-next.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/signals-backend/package.json b/plugins/signals-backend/package.json index 3995adb0dc..5d961c71ef 100644 --- a/plugins/signals-backend/package.json +++ b/plugins/signals-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals-backend", - "version": "0.1.3", + "version": "0.1.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/signals-node/CHANGELOG.md b/plugins/signals-node/CHANGELOG.md index 3eddc74c02..ea703d4291 100644 --- a/plugins/signals-node/CHANGELOG.md +++ b/plugins/signals-node/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-signals-node +## 0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/plugin-events-node@0.3.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.3.4-next.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/signals-node/package.json b/plugins/signals-node/package.json index 3101f0baa4..970b89aca2 100644 --- a/plugins/signals-node/package.json +++ b/plugins/signals-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-signals-node", "description": "Node.js library for the signals plugin", - "version": "0.1.3", + "version": "0.1.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/signals/CHANGELOG.md b/plugins/signals/CHANGELOG.md index 3e3623db6e..fdfddfdf2a 100644 --- a/plugins/signals/CHANGELOG.md +++ b/plugins/signals/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-signals +## 0.0.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## 0.0.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/types@1.1.1 + - @backstage/plugin-signals-react@0.0.3 + ## 0.0.5 ### Patch Changes diff --git a/plugins/signals/package.json b/plugins/signals/package.json index ec529e164f..7d5fb3bcf9 100644 --- a/plugins/signals/package.json +++ b/plugins/signals/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals", - "version": "0.0.5", + "version": "0.0.6-next.1", "backstage": { "role": "frontend-plugin" }, diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index 2b9872d5da..5151796171 100644 --- a/plugins/techdocs-addons-test-utils/CHANGELOG.md +++ b/plugins/techdocs-addons-test-utils/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-techdocs-addons-test-utils +## 1.0.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.19.1-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-search-react@1.7.11-next.1 + - @backstage/plugin-techdocs@1.10.5-next.1 + - @backstage/plugin-techdocs-react@1.2.4-next.1 + +## 1.0.32-next.0 + +### Patch Changes + +- 2f13862: Fix bug in TechDocsAddonTester when jest.resetAllMocks is called between tests +- Updated dependencies + - @backstage/plugin-catalog@1.19.1-next.0 + - @backstage/plugin-techdocs@1.10.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-techdocs-react@1.2.4-next.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/integration-react@1.1.26 + - @backstage/test-utils@1.5.5-next.0 + - @backstage/plugin-search-react@1.7.11-next.0 + ## 1.0.31 ### Patch Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index 869ab58154..6a6edcd5fa 100644 --- a/plugins/techdocs-addons-test-utils/package.json +++ b/plugins/techdocs-addons-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-addons-test-utils", - "version": "1.0.31", + "version": "1.0.32-next.1", "backstage": { "role": "web-library" }, diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index a3aefe34a1..53ee8b3681 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-techdocs-backend +## 1.10.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.1.23-next.1 + - @backstage/plugin-techdocs-node@1.12.4-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 1.10.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/catalog-client@1.6.5-next.0 + - @backstage/plugin-catalog-common@1.0.23-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.1.23-next.0 + - @backstage/plugin-techdocs-node@1.12.4-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/plugin-permission-common@0.7.13 + ## 1.10.4 ### Patch Changes diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index a620169515..013029bb0b 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "1.10.4", + "version": "1.10.5-next.1", "description": "The Backstage backend plugin that renders technical documentation for your components", "backstage": { "role": "backend-plugin" diff --git a/plugins/techdocs-module-addons-contrib/CHANGELOG.md b/plugins/techdocs-module-addons-contrib/CHANGELOG.md index 140ad1caa1..b914a8e3b1 100644 --- a/plugins/techdocs-module-addons-contrib/CHANGELOG.md +++ b/plugins/techdocs-module-addons-contrib/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-techdocs-module-addons-contrib +## 1.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-techdocs-react@1.2.4-next.1 + +## 1.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-techdocs-react@1.2.4-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/integration@1.10.0 + - @backstage/integration-react@1.1.26 + ## 1.1.9 ### Patch Changes diff --git a/plugins/techdocs-module-addons-contrib/package.json b/plugins/techdocs-module-addons-contrib/package.json index 385a9e0caf..5c8a0248ee 100644 --- a/plugins/techdocs-module-addons-contrib/package.json +++ b/plugins/techdocs-module-addons-contrib/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-module-addons-contrib", - "version": "1.1.9", + "version": "1.1.10-next.1", "description": "Plugin module for contributed TechDocs Addons", "backstage": { "role": "frontend-plugin-module" diff --git a/plugins/techdocs-node/CHANGELOG.md b/plugins/techdocs-node/CHANGELOG.md index 7ecdec30bc..23f5d333d3 100644 --- a/plugins/techdocs-node/CHANGELOG.md +++ b/plugins/techdocs-node/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-techdocs-node +## 1.12.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 1.12.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-search-common@1.2.11 + ## 1.12.3 ### Patch Changes diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index d8b91c5a98..1ff7b305f6 100644 --- a/plugins/techdocs-node/package.json +++ b/plugins/techdocs-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-node", - "version": "1.12.3", + "version": "1.12.4-next.1", "description": "Common node.js functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", "backstage": { "role": "node-library" diff --git a/plugins/techdocs-react/CHANGELOG.md b/plugins/techdocs-react/CHANGELOG.md index afae8eefb1..9a13bd2149 100644 --- a/plugins/techdocs-react/CHANGELOG.md +++ b/plugins/techdocs-react/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-techdocs-react +## 1.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + +## 1.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/version-bridge@1.0.8 + ## 1.2.3 ### Patch Changes diff --git a/plugins/techdocs-react/package.json b/plugins/techdocs-react/package.json index bf2dd4de0a..ebba33cdca 100644 --- a/plugins/techdocs-react/package.json +++ b/plugins/techdocs-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-react", - "version": "1.2.3", + "version": "1.2.4-next.1", "description": "Shared frontend utilities for TechDocs and Addons", "backstage": { "role": "web-library" @@ -25,7 +25,6 @@ "main": "src/index.ts", "types": "src/index.ts", "files": [ - "alpha", "dist" ], "scripts": { diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 5151259629..ee3071b030 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-techdocs +## 1.10.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-auth-react@0.1.2-next.1 + - @backstage/plugin-search-react@1.7.11-next.1 + - @backstage/plugin-techdocs-react@1.2.4-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## 1.10.5-next.0 + +### Patch Changes + +- d2cc139: Update path in Readme for Plugin Techdocs to show the correct setup information. +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-react@0.1.1-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-techdocs-react@1.2.4-next.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/integration@1.10.0 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-search-common@1.2.11 + - @backstage/plugin-search-react@1.7.11-next.0 + ## 1.10.4 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index e303bd9459..7e06c8f128 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "1.10.4", + "version": "1.10.5-next.1", "description": "The Backstage plugin that renders technical documentation for your components", "backstage": { "role": "frontend-plugin" diff --git a/plugins/user-settings-backend/CHANGELOG.md b/plugins/user-settings-backend/CHANGELOG.md index 9cc7292bd2..57e3663d5f 100644 --- a/plugins/user-settings-backend/CHANGELOG.md +++ b/plugins/user-settings-backend/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-user-settings-backend +## 0.2.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-auth-node@0.4.13-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## 0.2.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.4.13-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/backend-plugin-api@0.6.18-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.2.16 ### Patch Changes diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index 35eded315f..f8b46d8d3f 100644 --- a/plugins/user-settings-backend/package.json +++ b/plugins/user-settings-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings-backend", - "version": "0.2.16", + "version": "0.2.17-next.1", "description": "The Backstage backend plugin to manage user settings", "backstage": { "role": "backend-plugin" @@ -34,8 +34,7 @@ }, "files": [ "dist", - "migrations", - "alpha" + "migrations" ], "scripts": { "build": "backstage-cli package build", diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index 9e2b639714..2611a7d18a 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-user-settings +## 0.8.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## 0.8.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/core-app-api@1.12.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/types@1.1.1 + ## 0.8.5 ### Patch Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index fce7f86612..3461d220d6 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings", - "version": "0.8.5", + "version": "0.8.6-next.1", "description": "A Backstage plugin that provides a settings page", "backstage": { "role": "frontend-plugin" diff --git a/storybook/yarn.lock b/storybook/yarn.lock index 0b19785288..14a0fc7ee9 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -2842,90 +2842,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-darwin-arm64@npm:1.4.15" +"@swc/core-darwin-arm64@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-darwin-arm64@npm:1.5.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-darwin-x64@npm:1.4.15" +"@swc/core-darwin-x64@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-darwin-x64@npm:1.5.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.4.15" +"@swc/core-linux-arm-gnueabihf@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.5.0" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-arm64-gnu@npm:1.4.15" +"@swc/core-linux-arm64-gnu@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-arm64-gnu@npm:1.5.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-arm64-musl@npm:1.4.15" +"@swc/core-linux-arm64-musl@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-arm64-musl@npm:1.5.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-x64-gnu@npm:1.4.15" +"@swc/core-linux-x64-gnu@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-x64-gnu@npm:1.5.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-x64-musl@npm:1.4.15" +"@swc/core-linux-x64-musl@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-x64-musl@npm:1.5.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-win32-arm64-msvc@npm:1.4.15" +"@swc/core-win32-arm64-msvc@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-win32-arm64-msvc@npm:1.5.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-win32-ia32-msvc@npm:1.4.15" +"@swc/core-win32-ia32-msvc@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-win32-ia32-msvc@npm:1.5.0" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-win32-x64-msvc@npm:1.4.15" +"@swc/core-win32-x64-msvc@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-win32-x64-msvc@npm:1.5.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.46": - version: 1.4.15 - resolution: "@swc/core@npm:1.4.15" + version: 1.5.0 + resolution: "@swc/core@npm:1.5.0" dependencies: - "@swc/core-darwin-arm64": 1.4.15 - "@swc/core-darwin-x64": 1.4.15 - "@swc/core-linux-arm-gnueabihf": 1.4.15 - "@swc/core-linux-arm64-gnu": 1.4.15 - "@swc/core-linux-arm64-musl": 1.4.15 - "@swc/core-linux-x64-gnu": 1.4.15 - "@swc/core-linux-x64-musl": 1.4.15 - "@swc/core-win32-arm64-msvc": 1.4.15 - "@swc/core-win32-ia32-msvc": 1.4.15 - "@swc/core-win32-x64-msvc": 1.4.15 + "@swc/core-darwin-arm64": 1.5.0 + "@swc/core-darwin-x64": 1.5.0 + "@swc/core-linux-arm-gnueabihf": 1.5.0 + "@swc/core-linux-arm64-gnu": 1.5.0 + "@swc/core-linux-arm64-musl": 1.5.0 + "@swc/core-linux-x64-gnu": 1.5.0 + "@swc/core-linux-x64-musl": 1.5.0 + "@swc/core-win32-arm64-msvc": 1.5.0 + "@swc/core-win32-ia32-msvc": 1.5.0 + "@swc/core-win32-x64-msvc": 1.5.0 "@swc/counter": ^0.1.2 "@swc/types": ^0.1.5 peerDependencies: @@ -2954,7 +2954,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: b5aad99711b56ac14bb05674a54f487cffbe7fb902c14af5102c0d790be3a9a5b37bded8759d6917df37a2767bdc60133322b67ea72c5dc2796d2959dd678dd3 + checksum: 1abab0b57755020b60ebf2557629c23c65c3546e4cd80f9d02fbdf208d4169c9300a0229c088081b43a01e630c9d86d02f8c240c9fc2842eaed2a517770fcd1d languageName: node linkType: hard diff --git a/yarn.lock b/yarn.lock index af96b924bd..0eb13f4629 100644 --- a/yarn.lock +++ b/yarn.lock @@ -599,6 +599,55 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/client-ses@npm:^3.550.0": + version: 3.556.0 + resolution: "@aws-sdk/client-ses@npm:3.556.0" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/client-sts": 3.556.0 + "@aws-sdk/core": 3.556.0 + "@aws-sdk/credential-provider-node": 3.556.0 + "@aws-sdk/middleware-host-header": 3.535.0 + "@aws-sdk/middleware-logger": 3.535.0 + "@aws-sdk/middleware-recursion-detection": 3.535.0 + "@aws-sdk/middleware-user-agent": 3.540.0 + "@aws-sdk/region-config-resolver": 3.535.0 + "@aws-sdk/types": 3.535.0 + "@aws-sdk/util-endpoints": 3.540.0 + "@aws-sdk/util-user-agent-browser": 3.535.0 + "@aws-sdk/util-user-agent-node": 3.535.0 + "@smithy/config-resolver": ^2.2.0 + "@smithy/core": ^1.4.2 + "@smithy/fetch-http-handler": ^2.5.0 + "@smithy/hash-node": ^2.2.0 + "@smithy/invalid-dependency": ^2.2.0 + "@smithy/middleware-content-length": ^2.2.0 + "@smithy/middleware-endpoint": ^2.5.1 + "@smithy/middleware-retry": ^2.3.1 + "@smithy/middleware-serde": ^2.3.0 + "@smithy/middleware-stack": ^2.2.0 + "@smithy/node-config-provider": ^2.3.0 + "@smithy/node-http-handler": ^2.5.0 + "@smithy/protocol-http": ^3.3.0 + "@smithy/smithy-client": ^2.5.1 + "@smithy/types": ^2.12.0 + "@smithy/url-parser": ^2.2.0 + "@smithy/util-base64": ^2.3.0 + "@smithy/util-body-length-browser": ^2.2.0 + "@smithy/util-body-length-node": ^2.3.0 + "@smithy/util-defaults-mode-browser": ^2.2.1 + "@smithy/util-defaults-mode-node": ^2.3.1 + "@smithy/util-endpoints": ^1.2.0 + "@smithy/util-middleware": ^2.2.0 + "@smithy/util-retry": ^2.2.0 + "@smithy/util-utf8": ^2.3.0 + "@smithy/util-waiter": ^2.2.0 + tslib: ^2.6.2 + checksum: 913108e79061185faae51711b121df99da624f988f530af23c3e4270299be60771be83f507dc8ed4af4051765077ea038edab19e4d0d32e159c4e67faf5fc9f8 + languageName: node + linkType: hard + "@aws-sdk/client-sqs@npm:^3.350.0": version: 3.556.0 resolution: "@aws-sdk/client-sqs@npm:3.556.0" @@ -3520,6 +3569,18 @@ __metadata: languageName: unknown linkType: soft +"@backstage/catalog-client@npm:^1.6.4": + version: 1.6.4 + resolution: "@backstage/catalog-client@npm:1.6.4" + dependencies: + "@backstage/catalog-model": ^1.4.5 + "@backstage/errors": ^1.2.4 + cross-fetch: ^4.0.0 + uri-template: ^2.0.0 + checksum: af3537d04f0abd6e6f3e49c7623994cc83db6efb2776fff5d59faee26c598840486e42a99c2bbb4a1b6ff97ad97ac857e913830cad62cda7cd71eef74cf2e179 + languageName: node + linkType: hard + "@backstage/catalog-client@workspace:^, @backstage/catalog-client@workspace:packages/catalog-client": version: 0.0.0-use.local resolution: "@backstage/catalog-client@workspace:packages/catalog-client" @@ -3533,7 +3594,19 @@ __metadata: languageName: unknown linkType: soft -"@backstage/catalog-model@^1.4.3, @backstage/catalog-model@^1.4.5, @backstage/catalog-model@workspace:^, @backstage/catalog-model@workspace:packages/catalog-model": +"@backstage/catalog-model@npm:^1.4.3, @backstage/catalog-model@npm:^1.4.5": + version: 1.4.5 + resolution: "@backstage/catalog-model@npm:1.4.5" + dependencies: + "@backstage/errors": ^1.2.4 + "@backstage/types": ^1.1.1 + ajv: ^8.10.0 + lodash: ^4.17.21 + checksum: 34aaa4b82d29bf3b0a4b52552f8eb8189041df826f87a7bbdef5107a1efb8ce1f7eb1c1a343868718ca2af9be76d9f5184f6a92076d893d3a3951d16881647b7 + languageName: node + linkType: hard + +"@backstage/catalog-model@workspace:^, @backstage/catalog-model@workspace:packages/catalog-model": version: 0.0.0-use.local resolution: "@backstage/catalog-model@workspace:packages/catalog-model" dependencies: @@ -3785,7 +3858,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/config@^1.1.1, @backstage/config@workspace:^, @backstage/config@workspace:packages/config": +"@backstage/config@^1.1.1, @backstage/config@^1.2.0, @backstage/config@workspace:^, @backstage/config@workspace:packages/config": version: 0.0.0-use.local resolution: "@backstage/config@workspace:packages/config" dependencies: @@ -3857,7 +3930,109 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-components@^0.14.4, @backstage/core-components@workspace:^, @backstage/core-components@workspace:packages/core-components": +"@backstage/core-components@npm:^0.13.10": + version: 0.13.10 + resolution: "@backstage/core-components@npm:0.13.10" + dependencies: + "@backstage/config": ^1.1.1 + "@backstage/core-plugin-api": ^1.8.2 + "@backstage/errors": ^1.2.3 + "@backstage/theme": ^0.5.0 + "@backstage/version-bridge": ^1.0.7 + "@date-io/core": ^1.3.13 + "@material-table/core": ^3.1.0 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.61 + "@react-hookz/web": ^23.0.0 + "@types/react": ^16.13.1 || ^17.0.0 + "@types/react-sparklines": ^1.7.0 + "@types/react-text-truncate": ^0.14.0 + ansi-regex: ^6.0.1 + classnames: ^2.2.6 + d3-selection: ^3.0.0 + d3-shape: ^3.0.0 + d3-zoom: ^3.0.0 + dagre: ^0.8.5 + linkify-react: 4.1.3 + linkifyjs: 4.1.3 + lodash: ^4.17.21 + pluralize: ^8.0.0 + qs: ^6.9.4 + rc-progress: 3.5.1 + react-helmet: 6.1.0 + react-hook-form: ^7.12.2 + react-idle-timer: 5.6.2 + react-markdown: ^8.0.0 + react-sparklines: ^1.7.0 + react-syntax-highlighter: ^15.4.5 + react-text-truncate: ^0.19.0 + react-use: ^17.3.2 + react-virtualized-auto-sizer: ^1.0.11 + react-window: ^1.8.6 + remark-gfm: ^3.0.1 + zen-observable: ^0.10.0 + zod: ^3.22.4 + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: ec2a0d0a27bc4d6b9d4da97f0b4df600148529ee51bf2c8e7d3adc45c3950adac662535d3beabc451db346f2c7e3c412ceaa756fc774012b43dff5e2695f2271 + languageName: node + linkType: hard + +"@backstage/core-components@npm:^0.14.4": + version: 0.14.4 + resolution: "@backstage/core-components@npm:0.14.4" + dependencies: + "@backstage/config": ^1.2.0 + "@backstage/core-plugin-api": ^1.9.2 + "@backstage/errors": ^1.2.4 + "@backstage/theme": ^0.5.3 + "@backstage/version-bridge": ^1.0.8 + "@date-io/core": ^1.3.13 + "@material-table/core": ^3.1.0 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.61 + "@react-hookz/web": ^24.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 + "@types/react-sparklines": ^1.7.0 + "@types/react-text-truncate": ^0.14.0 + ansi-regex: ^6.0.1 + classnames: ^2.2.6 + d3-selection: ^3.0.0 + d3-shape: ^3.0.0 + d3-zoom: ^3.0.0 + dagre: ^0.8.5 + linkify-react: 4.1.3 + linkifyjs: 4.1.3 + lodash: ^4.17.21 + pluralize: ^8.0.0 + qs: ^6.9.4 + rc-progress: 3.5.1 + react-helmet: 6.1.0 + react-hook-form: ^7.12.2 + react-idle-timer: 5.7.2 + react-markdown: ^8.0.0 + react-sparklines: ^1.7.0 + react-syntax-highlighter: ^15.4.5 + react-text-truncate: ^0.19.0 + react-use: ^17.3.2 + react-virtualized-auto-sizer: ^1.0.11 + react-window: ^1.8.6 + remark-gfm: ^3.0.1 + zen-observable: ^0.10.0 + zod: ^3.22.4 + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: b96721b267daeec5a73a8b487f96ab5b5b3a85b8b113b2e45c5b733f3201af0d5cda3d7a325f5f68a4057ccbe78beac9f239b51ceb3099d3b0001709ec16928b + languageName: node + linkType: hard + +"@backstage/core-components@workspace:^, @backstage/core-components@workspace:packages/core-components": version: 0.0.0-use.local resolution: "@backstage/core-components@workspace:packages/core-components" dependencies: @@ -3930,57 +4105,6 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-components@npm:^0.13.10": - version: 0.13.10 - resolution: "@backstage/core-components@npm:0.13.10" - dependencies: - "@backstage/config": ^1.1.1 - "@backstage/core-plugin-api": ^1.8.2 - "@backstage/errors": ^1.2.3 - "@backstage/theme": ^0.5.0 - "@backstage/version-bridge": ^1.0.7 - "@date-io/core": ^1.3.13 - "@material-table/core": ^3.1.0 - "@material-ui/core": ^4.12.2 - "@material-ui/icons": ^4.9.1 - "@material-ui/lab": 4.0.0-alpha.61 - "@react-hookz/web": ^23.0.0 - "@types/react": ^16.13.1 || ^17.0.0 - "@types/react-sparklines": ^1.7.0 - "@types/react-text-truncate": ^0.14.0 - ansi-regex: ^6.0.1 - classnames: ^2.2.6 - d3-selection: ^3.0.0 - d3-shape: ^3.0.0 - d3-zoom: ^3.0.0 - dagre: ^0.8.5 - linkify-react: 4.1.3 - linkifyjs: 4.1.3 - lodash: ^4.17.21 - pluralize: ^8.0.0 - qs: ^6.9.4 - rc-progress: 3.5.1 - react-helmet: 6.1.0 - react-hook-form: ^7.12.2 - react-idle-timer: 5.6.2 - react-markdown: ^8.0.0 - react-sparklines: ^1.7.0 - react-syntax-highlighter: ^15.4.5 - react-text-truncate: ^0.19.0 - react-use: ^17.3.2 - react-virtualized-auto-sizer: ^1.0.11 - react-window: ^1.8.6 - remark-gfm: ^3.0.1 - zen-observable: ^0.10.0 - zod: ^3.22.4 - peerDependencies: - react: ^16.13.1 || ^17.0.0 || ^18.0.0 - react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 - react-router-dom: 6.0.0-beta.0 || ^6.3.0 - checksum: ec2a0d0a27bc4d6b9d4da97f0b4df600148529ee51bf2c8e7d3adc45c3950adac662535d3beabc451db346f2c7e3c412ceaa756fc774012b43dff5e2695f2271 - languageName: node - linkType: hard - "@backstage/core-plugin-api@^1.8.2, @backstage/core-plugin-api@^1.9.2, @backstage/core-plugin-api@workspace:^, @backstage/core-plugin-api@workspace:packages/core-plugin-api": version: 0.0.0-use.local resolution: "@backstage/core-plugin-api@workspace:packages/core-plugin-api" @@ -4128,6 +4252,26 @@ __metadata: languageName: unknown linkType: soft +"@backstage/frontend-plugin-api@npm:^0.6.4": + version: 0.6.4 + resolution: "@backstage/frontend-plugin-api@npm:0.6.4" + dependencies: + "@backstage/core-components": ^0.14.4 + "@backstage/core-plugin-api": ^1.9.2 + "@backstage/types": ^1.1.1 + "@backstage/version-bridge": ^1.0.8 + "@material-ui/core": ^4.12.4 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 + lodash: ^4.17.21 + zod: ^3.22.4 + zod-to-json-schema: ^3.21.4 + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 2ae2919147dcfd8a5b4379059ecb76461a27f38159c398915384db7a7396bc65451cf696d955e4ba23eb6dfb549395624d09412d0c9c776072d3f003e5f1cba8 + languageName: node + linkType: hard + "@backstage/frontend-plugin-api@workspace:^, @backstage/frontend-plugin-api@workspace:packages/frontend-plugin-api": version: 0.0.0-use.local resolution: "@backstage/frontend-plugin-api@workspace:packages/frontend-plugin-api" @@ -4191,7 +4335,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/integration-react@workspace:^, @backstage/integration-react@workspace:packages/integration-react": +"@backstage/integration-react@^1.1.26, @backstage/integration-react@workspace:^, @backstage/integration-react@workspace:packages/integration-react": version: 0.0.0-use.local resolution: "@backstage/integration-react@workspace:packages/integration-react" dependencies: @@ -5267,7 +5411,18 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-catalog-common@^1.0.20, @backstage/plugin-catalog-common@workspace:^, @backstage/plugin-catalog-common@workspace:plugins/catalog-common": +"@backstage/plugin-catalog-common@npm:^1.0.20, @backstage/plugin-catalog-common@npm:^1.0.22": + version: 1.0.22 + resolution: "@backstage/plugin-catalog-common@npm:1.0.22" + dependencies: + "@backstage/catalog-model": ^1.4.5 + "@backstage/plugin-permission-common": ^0.7.13 + "@backstage/plugin-search-common": ^1.2.11 + checksum: f468ade184d5e535cc27cbb27a9dbd6cd21c1601b5a84167d2ea1004f471180ef8bf148df5561b5557c332bdc01480d020e93f62915f029cc728802cebf8e255 + languageName: node + linkType: hard + +"@backstage/plugin-catalog-common@workspace:^, @backstage/plugin-catalog-common@workspace:plugins/catalog-common": version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-common@workspace:plugins/catalog-common" dependencies: @@ -5375,7 +5530,43 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-catalog-react@^1.11.3, @backstage/plugin-catalog-react@^1.9.3, @backstage/plugin-catalog-react@workspace:^, @backstage/plugin-catalog-react@workspace:plugins/catalog-react": +"@backstage/plugin-catalog-react@npm:^1.11.3, @backstage/plugin-catalog-react@npm:^1.9.3": + version: 1.11.3 + resolution: "@backstage/plugin-catalog-react@npm:1.11.3" + dependencies: + "@backstage/catalog-client": ^1.6.4 + "@backstage/catalog-model": ^1.4.5 + "@backstage/core-components": ^0.14.4 + "@backstage/core-plugin-api": ^1.9.2 + "@backstage/errors": ^1.2.4 + "@backstage/frontend-plugin-api": ^0.6.4 + "@backstage/integration-react": ^1.1.26 + "@backstage/plugin-catalog-common": ^1.0.22 + "@backstage/plugin-permission-common": ^0.7.13 + "@backstage/plugin-permission-react": ^0.4.22 + "@backstage/types": ^1.1.1 + "@backstage/version-bridge": ^1.0.8 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.61 + "@react-hookz/web": ^24.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 + classnames: ^2.2.6 + lodash: ^4.17.21 + material-ui-popup-state: ^1.9.3 + qs: ^6.9.4 + react-use: ^17.2.4 + yaml: ^2.0.0 + zen-observable: ^0.10.0 + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: d04919bff692094bb145d8479aae47368911f3adc92644b4742e5db1296af9a2673cf4341995b9c65e660cd608d3b59a136b9c164ab2df51bdc3ccbaf5af71fd + languageName: node + linkType: hard + +"@backstage/plugin-catalog-react@workspace:^, @backstage/plugin-catalog-react@workspace:plugins/catalog-react": version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-react@workspace:plugins/catalog-react" dependencies: @@ -5982,6 +6173,30 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-notifications-backend-module-email@workspace:plugins/notifications-backend-module-email": + version: 0.0.0-use.local + resolution: "@backstage/plugin-notifications-backend-module-email@workspace:plugins/notifications-backend-module-email" + dependencies: + "@aws-sdk/client-ses": ^3.550.0 + "@aws-sdk/types": ^3.347.0 + "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/catalog-client": "workspace:^" + "@backstage/catalog-model": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/integration-aws-node": "workspace:^" + "@backstage/plugin-notifications-common": "workspace:^" + "@backstage/plugin-notifications-node": "workspace:^" + "@backstage/types": "workspace:^" + "@types/nodemailer": ^6.4.14 + lodash: ^4.17.21 + nodemailer: ^6.9.13 + p-throttle: ^4.1.1 + languageName: unknown + linkType: soft + "@backstage/plugin-notifications-backend@workspace:^, @backstage/plugin-notifications-backend@workspace:plugins/notifications-backend": version: 0.0.0-use.local resolution: "@backstage/plugin-notifications-backend@workspace:plugins/notifications-backend" @@ -6057,6 +6272,7 @@ __metadata: "@backstage/dev-utils": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/plugin-notifications-common": "workspace:^" + "@backstage/plugin-signals": "workspace:^" "@backstage/plugin-signals-react": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" @@ -6069,7 +6285,9 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/react": ^16.13.1 || ^17.0.0 lodash: ^4.17.21 + material-ui-confirm: ^3.0.12 msw: ^1.0.0 + notistack: ^3.0.1 react-relative-time: ^0.0.9 react-use: ^17.2.4 peerDependencies: @@ -6187,7 +6405,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-permission-common@workspace:^, @backstage/plugin-permission-common@workspace:plugins/permission-common": +"@backstage/plugin-permission-common@^0.7.13, @backstage/plugin-permission-common@workspace:^, @backstage/plugin-permission-common@workspace:plugins/permission-common": version: 0.0.0-use.local resolution: "@backstage/plugin-permission-common@workspace:plugins/permission-common" dependencies: @@ -6225,7 +6443,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-permission-react@workspace:^, @backstage/plugin-permission-react@workspace:plugins/permission-react": +"@backstage/plugin-permission-react@^0.4.22, @backstage/plugin-permission-react@workspace:^, @backstage/plugin-permission-react@workspace:plugins/permission-react": version: 0.0.0-use.local resolution: "@backstage/plugin-permission-react@workspace:plugins/permission-react" dependencies: @@ -6591,6 +6809,7 @@ __metadata: uuid: ^9.0.0 wait-for-expect: ^3.0.2 winston: ^3.2.1 + winston-transport: ^4.7.0 yaml: ^2.0.0 zen-observable: ^0.10.0 zod: ^3.22.4 @@ -6940,7 +7159,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-search-common@workspace:^, @backstage/plugin-search-common@workspace:plugins/search-common": +"@backstage/plugin-search-common@^1.2.11, @backstage/plugin-search-common@workspace:^, @backstage/plugin-search-common@workspace:plugins/search-common": version: 0.0.0-use.local resolution: "@backstage/plugin-search-common@workspace:plugins/search-common" dependencies: @@ -7419,6 +7638,7 @@ __metadata: "@types/is-glob": ^4.0.2 "@types/node": ^18.17.8 "@types/prettier": ^2.0.0 + "@useoptic/openapi-utilities": ^0.54.8 chalk: ^4.0.0 codeowners-utils: ^1.0.2 command-exists: ^1.2.9 @@ -7475,7 +7695,23 @@ __metadata: languageName: unknown linkType: soft -"@backstage/theme@^0.5.0, @backstage/theme@workspace:^, @backstage/theme@workspace:packages/theme": +"@backstage/theme@npm:^0.5.0, @backstage/theme@npm:^0.5.3": + version: 0.5.3 + resolution: "@backstage/theme@npm:0.5.3" + dependencies: + "@emotion/react": ^11.10.5 + "@emotion/styled": ^11.10.5 + "@mui/material": ^5.12.2 + peerDependencies: + "@material-ui/core": ^4.12.2 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + checksum: ac6c3bbd73294385f73aa91e04f8bf3a1bb78cadc0e43034760ebf19e86814ed2d679f2641bb086aa9e305a24d923d8c3480b2de3bff55189c19bc5dfdce1814 + languageName: node + linkType: hard + +"@backstage/theme@workspace:^, @backstage/theme@workspace:packages/theme": version: 0.0.0-use.local resolution: "@backstage/theme@workspace:packages/theme" dependencies: @@ -7506,7 +7742,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/version-bridge@^1.0.7, @backstage/version-bridge@workspace:^, @backstage/version-bridge@workspace:packages/version-bridge": +"@backstage/version-bridge@^1.0.7, @backstage/version-bridge@^1.0.8, @backstage/version-bridge@workspace:^, @backstage/version-bridge@workspace:packages/version-bridge": version: 0.0.0-use.local resolution: "@backstage/version-bridge@workspace:packages/version-bridge" dependencies: @@ -7581,7 +7817,7 @@ __metadata: languageName: node linkType: hard -"@changesets/assemble-release-plan@npm:^6.0.0": +"@changesets/assemble-release-plan@npm:6.0.0": version: 6.0.0 resolution: "@changesets/assemble-release-plan@npm:6.0.0" dependencies: @@ -7595,6 +7831,20 @@ __metadata: languageName: node linkType: hard +"@changesets/assemble-release-plan@patch:@changesets/assemble-release-plan@npm%3A6.0.0#./.yarn/patches/@changesets-assemble-release-plan-npm-6.0.0-f7b3005037.patch::locator=root%40workspace%3A.": + version: 6.0.0 + resolution: "@changesets/assemble-release-plan@patch:@changesets/assemble-release-plan@npm%3A6.0.0#./.yarn/patches/@changesets-assemble-release-plan-npm-6.0.0-f7b3005037.patch::version=6.0.0&hash=43c5e4&locator=root%40workspace%3A." + dependencies: + "@babel/runtime": ^7.20.1 + "@changesets/errors": ^0.2.0 + "@changesets/get-dependents-graph": ^2.0.0 + "@changesets/types": ^6.0.0 + "@manypkg/get-packages": ^1.1.3 + semver: ^7.5.3 + checksum: b7a68e28d03379bdc2a1d7171963990e1b88d0e5efca5f5ed490c0f583d66d0ccbd4c04ebf4e5cd1c38e1346ff0e5c4c16e1b4973cdd97fabdbad0c6996fe016 + languageName: node + linkType: hard + "@changesets/changelog-git@npm:^0.2.0": version: 0.2.0 resolution: "@changesets/changelog-git@npm:0.2.0" @@ -12004,24 +12254,6 @@ __metadata: languageName: node linkType: hard -"@puppeteer/browsers@npm:2.2.1": - version: 2.2.1 - resolution: "@puppeteer/browsers@npm:2.2.1" - dependencies: - debug: 4.3.4 - extract-zip: 2.0.1 - progress: 2.0.3 - proxy-agent: 6.4.0 - semver: 7.6.0 - tar-fs: 3.0.5 - unbzip2-stream: 1.4.3 - yargs: 17.7.2 - bin: - browsers: lib/cjs/main-cli.js - checksum: c2ec8bac9978ae6279d67442a3a81c517db1e172bd4603d1983eb48de12b088d8b565b1817abe21ba6df76be9a68e3fc543d4c7111c964a93f3ac9b14f7c76e5 - languageName: node - linkType: hard - "@radix-ui/primitive@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/primitive@npm:1.0.1" @@ -14712,90 +14944,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-darwin-arm64@npm:1.4.15" +"@swc/core-darwin-arm64@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-darwin-arm64@npm:1.5.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-darwin-x64@npm:1.4.15" +"@swc/core-darwin-x64@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-darwin-x64@npm:1.5.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.4.15" +"@swc/core-linux-arm-gnueabihf@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.5.0" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-arm64-gnu@npm:1.4.15" +"@swc/core-linux-arm64-gnu@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-arm64-gnu@npm:1.5.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-arm64-musl@npm:1.4.15" +"@swc/core-linux-arm64-musl@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-arm64-musl@npm:1.5.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-x64-gnu@npm:1.4.15" +"@swc/core-linux-x64-gnu@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-x64-gnu@npm:1.5.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-linux-x64-musl@npm:1.4.15" +"@swc/core-linux-x64-musl@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-linux-x64-musl@npm:1.5.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-win32-arm64-msvc@npm:1.4.15" +"@swc/core-win32-arm64-msvc@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-win32-arm64-msvc@npm:1.5.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-win32-ia32-msvc@npm:1.4.15" +"@swc/core-win32-ia32-msvc@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-win32-ia32-msvc@npm:1.5.0" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.4.15": - version: 1.4.15 - resolution: "@swc/core-win32-x64-msvc@npm:1.4.15" +"@swc/core-win32-x64-msvc@npm:1.5.0": + version: 1.5.0 + resolution: "@swc/core-win32-x64-msvc@npm:1.5.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.46": - version: 1.4.15 - resolution: "@swc/core@npm:1.4.15" + version: 1.5.0 + resolution: "@swc/core@npm:1.5.0" dependencies: - "@swc/core-darwin-arm64": 1.4.15 - "@swc/core-darwin-x64": 1.4.15 - "@swc/core-linux-arm-gnueabihf": 1.4.15 - "@swc/core-linux-arm64-gnu": 1.4.15 - "@swc/core-linux-arm64-musl": 1.4.15 - "@swc/core-linux-x64-gnu": 1.4.15 - "@swc/core-linux-x64-musl": 1.4.15 - "@swc/core-win32-arm64-msvc": 1.4.15 - "@swc/core-win32-ia32-msvc": 1.4.15 - "@swc/core-win32-x64-msvc": 1.4.15 + "@swc/core-darwin-arm64": 1.5.0 + "@swc/core-darwin-x64": 1.5.0 + "@swc/core-linux-arm-gnueabihf": 1.5.0 + "@swc/core-linux-arm64-gnu": 1.5.0 + "@swc/core-linux-arm64-musl": 1.5.0 + "@swc/core-linux-x64-gnu": 1.5.0 + "@swc/core-linux-x64-musl": 1.5.0 + "@swc/core-win32-arm64-msvc": 1.5.0 + "@swc/core-win32-ia32-msvc": 1.5.0 + "@swc/core-win32-x64-msvc": 1.5.0 "@swc/counter": ^0.1.2 "@swc/types": ^0.1.5 peerDependencies: @@ -14824,7 +15056,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: b5aad99711b56ac14bb05674a54f487cffbe7fb902c14af5102c0d790be3a9a5b37bded8759d6917df37a2767bdc60133322b67ea72c5dc2796d2959dd678dd3 + checksum: 1abab0b57755020b60ebf2557629c23c65c3546e4cd80f9d02fbdf208d4169c9300a0229c088081b43a01e630c9d86d02f8c240c9fc2842eaed2a517770fcd1d languageName: node linkType: hard @@ -16063,6 +16295,15 @@ __metadata: languageName: node linkType: hard +"@types/nodemailer@npm:^6.4.14": + version: 6.4.14 + resolution: "@types/nodemailer@npm:6.4.14" + dependencies: + "@types/node": "*" + checksum: 5f61f01dd736b17f431d1e8b320322f86460604b45df947fc4bc8999d7c7719405e349f7abba86e4fb100a464a30b52615d00dac03d9cb37562ff04487ebd310 + languageName: node + linkType: hard + "@types/normalize-package-data@npm:^2.4.0": version: 2.4.1 resolution: "@types/normalize-package-data@npm:2.4.1" @@ -16236,15 +16477,6 @@ __metadata: languageName: node linkType: hard -"@types/puppeteer@npm:^7.0.0": - version: 7.0.4 - resolution: "@types/puppeteer@npm:7.0.4" - dependencies: - puppeteer: "*" - checksum: c84a44b054454c13935a9cf0f8983166e238532397af4f321c918d89b43a91f854460e3d0dda122f72c258b444dbcdc04ada950e35adc1938ff3b7831c6fd7a4 - languageName: node - linkType: hard - "@types/qs@npm:*": version: 6.9.6 resolution: "@types/qs@npm:6.9.6" @@ -16633,12 +16865,12 @@ __metadata: linkType: hard "@types/tar@npm:^6.1.1": - version: 6.1.12 - resolution: "@types/tar@npm:6.1.12" + version: 6.1.13 + resolution: "@types/tar@npm:6.1.13" dependencies: "@types/node": "*" minipass: ^4.0.0 - checksum: b1cbae1894cc943e3a86f88613853986f97f552c6bec34ee990a47fe5905871d1552397ff440108233d75d05653be2fbc356e62beb0b93a45b927fb88060b438 + checksum: bb3910936a6b37f093e38b73a52b0544fd73079685f5ea72e5c049fddc3770e58d80cf6d714425853f0746290221852c1a7ca89ffdb9614f3b2a858a3bf5436a languageName: node linkType: hard @@ -16822,7 +17054,7 @@ __metadata: languageName: node linkType: hard -"@types/yauzl@npm:^2.10.0, @types/yauzl@npm:^2.9.1": +"@types/yauzl@npm:^2.10.0": version: 2.10.3 resolution: "@types/yauzl@npm:2.10.3" dependencies: @@ -17095,6 +17327,16 @@ __metadata: languageName: node linkType: hard +"@useoptic/json-pointer-helpers@npm:0.54.8": + version: 0.54.8 + resolution: "@useoptic/json-pointer-helpers@npm:0.54.8" + dependencies: + jsonpointer: ^5.0.1 + minimatch: 9.0.3 + checksum: 4eddabb6dce3ca8160dcd4904299b6964945c3fe47d39bfeca6c68b9a50b058b901a6fb10ab168295475d651df3349149faa5f27f77293e15b6eee8d4417432e + languageName: node + linkType: hard + "@useoptic/openapi-io@npm:0.50.10": version: 0.50.10 resolution: "@useoptic/openapi-io@npm:0.50.10" @@ -17149,6 +17391,31 @@ __metadata: languageName: node linkType: hard +"@useoptic/openapi-utilities@npm:^0.54.8": + version: 0.54.8 + resolution: "@useoptic/openapi-utilities@npm:0.54.8" + dependencies: + "@useoptic/json-pointer-helpers": 0.54.8 + ajv: ^8.6.0 + ajv-errors: ~3.0.0 + ajv-formats: ~2.1.0 + chalk: ^4.1.2 + fast-deep-equal: ^3.1.3 + is-url: ^1.2.4 + js-yaml: ^4.1.0 + json-stable-stringify: ^1.0.1 + lodash.groupby: ^4.6.0 + lodash.isequal: ^4.5.0 + lodash.omit: ^4.5.0 + node-machine-id: ^1.1.12 + openapi-types: ^12.0.2 + ts-invariant: ^0.9.3 + url-join: ^4.0.1 + yaml-ast-parser: ^0.0.43 + checksum: fa9e9f430c77687591aaf8b43b7b31a7c2f80fe9c140aaa978f1948f84d3e974181c91c3d8ec3e06efca9735c7826290baf4be72063bf733887aa632b40c3c4a + languageName: node + linkType: hard + "@useoptic/optic@npm:^0.50.10": version: 0.50.10 resolution: "@useoptic/optic@npm:0.50.10" @@ -19361,7 +19628,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.2.1, buffer@npm:^5.5.0, buffer@npm:^5.7.1": +"buffer@npm:^5.5.0, buffer@npm:^5.7.1": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -19851,19 +20118,6 @@ __metadata: languageName: node linkType: hard -"chromium-bidi@npm:0.5.17": - version: 0.5.17 - resolution: "chromium-bidi@npm:0.5.17" - dependencies: - mitt: 3.0.1 - urlpattern-polyfill: 10.0.0 - zod: 3.22.4 - peerDependencies: - devtools-protocol: "*" - checksum: 522da996ed5abfb47707583cc24785f9aa05d87bd968dbd520f245cf8972fa3ec102f8d1d72fa07558daa70495d8c6f2bf364d8599eb60b77504e528601d8a30 - languageName: node - linkType: hard - "ci-info@npm:^2.0.0": version: 2.0.0 resolution: "ci-info@npm:2.0.0" @@ -20123,7 +20377,7 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^1.0.2, clsx@npm:^1.0.4, clsx@npm:^1.1.1, clsx@npm:^1.2.1": +"clsx@npm:^1.0.2, clsx@npm:^1.0.4, clsx@npm:^1.1.0, clsx@npm:^1.1.1, clsx@npm:^1.2.1": version: 1.2.1 resolution: "clsx@npm:1.2.1" checksum: 30befca8019b2eb7dbad38cff6266cf543091dae2825c856a62a8ccf2c3ab9c2907c4d12b288b73101196767f66812365400a227581484a05f968b0307cfaf12 @@ -20872,23 +21126,6 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:9.0.0": - version: 9.0.0 - resolution: "cosmiconfig@npm:9.0.0" - dependencies: - env-paths: ^2.2.1 - import-fresh: ^3.3.0 - js-yaml: ^4.1.0 - parse-json: ^5.2.0 - peerDependencies: - typescript: ">=4.9.5" - peerDependenciesMeta: - typescript: - optional: true - checksum: a30c424b53d442ea0bdd24cb1b3d0d8687c8dda4a17ab6afcdc439f8964438801619cdb66e8e79f63b9caa3e6586b60d8bab9ce203e72df6c5e80179b971fe8f - languageName: node - linkType: hard - "cosmiconfig@npm:^6.0.0": version: 6.0.0 resolution: "cosmiconfig@npm:6.0.0" @@ -22129,13 +22366,6 @@ __metadata: languageName: node linkType: hard -"devtools-protocol@npm:0.0.1262051": - version: 0.0.1262051 - resolution: "devtools-protocol@npm:0.0.1262051" - checksum: beaad00059964a661ab056d5e993492742c612c0370c6f08acd91490181c4d4ecf57d316eedb5a37fb6bb59321901d09ce50762f79ea09a50751d86f601b8f8e - languageName: node - linkType: hard - "dezalgo@npm:^1.0.0, dezalgo@npm:^1.0.4": version: 1.0.4 resolution: "dezalgo@npm:1.0.4" @@ -22545,7 +22775,6 @@ __metadata: "@backstage/errors": "workspace:^" "@types/fs-extra": ^11.0.0 "@types/node": ^18.17.8 - "@types/puppeteer": ^7.0.0 chalk: ^4.0.0 commander: ^12.0.0 cross-fetch: ^4.0.0 @@ -22628,13 +22857,13 @@ __metadata: linkType: hard "ejs@npm:^3.1.6": - version: 3.1.9 - resolution: "ejs@npm:3.1.9" + version: 3.1.10 + resolution: "ejs@npm:3.1.10" dependencies: jake: ^10.8.5 bin: ejs: bin/cli.js - checksum: af6f10eb815885ff8a8cfacc42c6b6cf87daf97a4884f87a30e0c3271fedd85d76a3a297d9c33a70e735b97ee632887f85e32854b9cdd3a2d97edf931519a35f + checksum: ce90637e9c7538663ae023b8a7a380b2ef7cc4096de70be85abf5a3b9641912dde65353211d05e24d56b1f242d71185c6d00e02cb8860701d571786d92c71f05 languageName: node linkType: hard @@ -24295,23 +24524,6 @@ __metadata: languageName: node linkType: hard -"extract-zip@npm:2.0.1": - version: 2.0.1 - resolution: "extract-zip@npm:2.0.1" - dependencies: - "@types/yauzl": ^2.9.1 - debug: ^4.1.1 - get-stream: ^5.1.0 - yauzl: ^2.10.0 - dependenciesMeta: - "@types/yauzl": - optional: true - bin: - extract-zip: cli.js - checksum: 8cbda9debdd6d6980819cc69734d874ddd71051c9fe5bde1ef307ebcedfe949ba57b004894b585f758b7c9eeeea0e3d87f2dda89b7d25320459c2c9643ebb635 - languageName: node - linkType: hard - "extsprintf@npm:1.3.0": version: 1.3.0 resolution: "extsprintf@npm:1.3.0" @@ -24544,15 +24756,6 @@ __metadata: languageName: node linkType: hard -"fd-slicer@npm:~1.1.0": - version: 1.1.0 - resolution: "fd-slicer@npm:1.1.0" - dependencies: - pend: ~1.2.0 - checksum: c8585fd5713f4476eb8261150900d2cb7f6ff2d87f8feb306ccc8a1122efd152f1783bdb2b8dc891395744583436bfd8081d8e63ece0ec8687eeefea394d4ff2 - languageName: node - linkType: hard - "fecha@npm:^4.2.0": version: 4.2.0 resolution: "fecha@npm:4.2.0" @@ -25649,6 +25852,15 @@ __metadata: languageName: node linkType: hard +"goober@npm:^2.0.33": + version: 2.1.14 + resolution: "goober@npm:2.1.14" + peerDependencies: + csstype: ^3.0.10 + checksum: 78978b7192d6a1af5cfbf1fd64b661b5f53ee6c733554b1f1b2ad3e1e2c979847fc080434390647640bb8358c0b193895d0007432c0886d12001f02f8f56b5e6 + languageName: node + linkType: hard + "google-auth-library@npm:^9.0.0, google-auth-library@npm:^9.6.3": version: 9.8.0 resolution: "google-auth-library@npm:9.8.0" @@ -26410,7 +26622,7 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.1": +"http-proxy-agent@npm:^7.0.0": version: 7.0.2 resolution: "http-proxy-agent@npm:7.0.2" dependencies: @@ -26508,7 +26720,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:7.0.4, https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.2, https-proxy-agent@npm:^7.0.3": +"https-proxy-agent@npm:7.0.4, https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.2": version: 7.0.4 resolution: "https-proxy-agent@npm:7.0.4" dependencies: @@ -30378,6 +30590,17 @@ __metadata: languageName: node linkType: hard +"material-ui-confirm@npm:^3.0.12": + version: 3.0.12 + resolution: "material-ui-confirm@npm:3.0.12" + peerDependencies: + "@mui/material": ">= 5.0.0" + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + checksum: de1092fdc4ab8104fbfdb5034921aeb2a5db5854ad5e31fb24dc4c204c6fb5b66a33ad377abf7adda390c432aefe96a2188ef1b8a3b9c1a80a9a28af63833458 + languageName: node + linkType: hard + "material-ui-popup-state@npm:^1.9.3": version: 1.9.3 resolution: "material-ui-popup-state@npm:1.9.3" @@ -31476,13 +31699,6 @@ __metadata: languageName: node linkType: hard -"mitt@npm:3.0.1": - version: 3.0.1 - resolution: "mitt@npm:3.0.1" - checksum: b55a489ac9c2949ab166b7f060601d3b6d893a852515ae9eca4e11df01c013876df777ea109317622b5c1c60e8aae252558e33c8c94e14124db38f64a39614b1 - languageName: node - linkType: hard - "mixme@npm:^0.5.1": version: 0.5.4 resolution: "mixme@npm:0.5.4" @@ -32253,6 +32469,13 @@ __metadata: languageName: node linkType: hard +"nodemailer@npm:^6.9.13": + version: 6.9.13 + resolution: "nodemailer@npm:6.9.13" + checksum: 1b591ef480be2ff69480127cbff819e6593b1ef263b6f920e1a4e83e40280582daf7a14a809ef92f9828e2a70bdb3ce22b11924e209f2afe4975f9ff37e08e9d + languageName: node + linkType: hard + "nodemon@npm:^3.0.1": version: 3.1.0 resolution: "nodemon@npm:3.1.0" @@ -32360,6 +32583,19 @@ __metadata: languageName: node linkType: hard +"notistack@npm:^3.0.1": + version: 3.0.1 + resolution: "notistack@npm:3.0.1" + dependencies: + clsx: ^1.1.0 + goober: ^2.0.33 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 421c970308690b8c8cb2e275e7f66020db7c1955e104f638e7fa562396a6b9322ff95f0e62492b07f3d36b0ef72adb4de2c2ce9803089c1c8f028d1a3b088e01 + languageName: node + linkType: hard + "npm-bundled@npm:^1.1.1": version: 1.1.2 resolution: "npm-bundled@npm:1.1.2" @@ -33165,6 +33401,13 @@ __metadata: languageName: node linkType: hard +"p-throttle@npm:^4.1.1": + version: 4.1.1 + resolution: "p-throttle@npm:4.1.1" + checksum: fe8709f3c3b1da7c033479375c2c302e80c1a5d86449013afa7cd46d1dc210bc824a7e4a9d088e66d31987d00878c2b5491bb2fe76246d4d2fc9a1636f5f8298 + languageName: node + linkType: hard + "p-timeout@npm:^3.2.0": version: 3.2.0 resolution: "p-timeout@npm:3.2.0" @@ -33191,7 +33434,7 @@ __metadata: languageName: node linkType: hard -"pac-proxy-agent@npm:^7.0.0, pac-proxy-agent@npm:^7.0.1": +"pac-proxy-agent@npm:^7.0.0": version: 7.0.1 resolution: "pac-proxy-agent@npm:7.0.1" dependencies: @@ -34834,13 +35077,6 @@ __metadata: languageName: node linkType: hard -"progress@npm:2.0.3": - version: 2.0.3 - resolution: "progress@npm:2.0.3" - checksum: f67403fe7b34912148d9252cb7481266a354bd99ce82c835f79070643bb3c6583d10dbcfda4d41e04bbc1d8437e9af0fb1e1f2135727878f5308682a579429b7 - languageName: node - linkType: hard - "prom-client@npm:^15.0.0": version: 15.1.2 resolution: "prom-client@npm:15.1.2" @@ -35028,22 +35264,6 @@ __metadata: languageName: node linkType: hard -"proxy-agent@npm:6.4.0": - version: 6.4.0 - resolution: "proxy-agent@npm:6.4.0" - dependencies: - agent-base: ^7.0.2 - debug: ^4.3.4 - http-proxy-agent: ^7.0.1 - https-proxy-agent: ^7.0.3 - lru-cache: ^7.14.1 - pac-proxy-agent: ^7.0.1 - proxy-from-env: ^1.1.0 - socks-proxy-agent: ^8.0.2 - checksum: 4d3794ad5e07486298902f0a7f250d0f869fa0e92d790767ca3f793a81374ce0ab6c605f8ab8e791c4d754da96656b48d1c24cb7094bfd310a15867e4a0841d7 - languageName: node - linkType: hard - "proxy-from-env@npm:^1.1.0": version: 1.1.0 resolution: "proxy-from-env@npm:1.1.0" @@ -35126,33 +35346,6 @@ __metadata: languageName: node linkType: hard -"puppeteer-core@npm:22.6.4": - version: 22.6.4 - resolution: "puppeteer-core@npm:22.6.4" - dependencies: - "@puppeteer/browsers": 2.2.1 - chromium-bidi: 0.5.17 - debug: 4.3.4 - devtools-protocol: 0.0.1262051 - ws: 8.16.0 - checksum: 76d4328e7d2a788a7a2dc3a8b19571f8eda842ff990da2fe773c3e5c01236a03856b67d9a2a6ae976f8fa74ab38813e2c7e7c4b15b9715965b2156e9ca1f9a78 - languageName: node - linkType: hard - -"puppeteer@npm:*": - version: 22.6.4 - resolution: "puppeteer@npm:22.6.4" - dependencies: - "@puppeteer/browsers": 2.2.1 - cosmiconfig: 9.0.0 - devtools-protocol: 0.0.1262051 - puppeteer-core: 22.6.4 - bin: - puppeteer: lib/esm/puppeteer/node/cli.js - checksum: 7dcbda7fc3b999f3ee7808d1c67da41be0aeabd66b0831711a46a897a2681d117f044263e57aeb7d0a096c420bb9f5871c5c2b21c38e40d323fa675e1de322c9 - languageName: node - linkType: hard - "pure-color@npm:^1.2.0": version: 1.3.0 resolution: "pure-color@npm:1.3.0" @@ -37503,7 +37696,16 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.6.0, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.4.0, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": +"semver@npm:^6.0.0, semver@npm:^6.1.0, semver@npm:^6.2.0, semver@npm:^6.3.0, semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" + bin: + semver: bin/semver.js + checksum: ae47d06de28836adb9d3e25f22a92943477371292d9b665fb023fae278d345d508ca1958232af086d85e0155aee22e313e100971898bbb8d5d89b8b1d4054ca2 + languageName: node + linkType: hard + +"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.4.0, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": version: 7.6.0 resolution: "semver@npm:7.6.0" dependencies: @@ -37514,15 +37716,6 @@ __metadata: languageName: node linkType: hard -"semver@npm:^6.0.0, semver@npm:^6.1.0, semver@npm:^6.2.0, semver@npm:^6.3.0, semver@npm:^6.3.1": - version: 6.3.1 - resolution: "semver@npm:6.3.1" - bin: - semver: bin/semver.js - checksum: ae47d06de28836adb9d3e25f22a92943477371292d9b665fb023fae278d345d508ca1958232af086d85e0155aee22e313e100971898bbb8d5d89b8b1d4054ca2 - languageName: node - linkType: hard - "send@npm:0.18.0": version: 0.18.0 resolution: "send@npm:0.18.0" @@ -39263,7 +39456,19 @@ __metadata: languageName: node linkType: hard -"tar-fs@npm:3.0.5, tar-fs@npm:^3.0.5": +"tar-fs@npm:^2.0.0": + version: 2.1.1 + resolution: "tar-fs@npm:2.1.1" + dependencies: + chownr: ^1.1.1 + mkdirp-classic: ^0.5.2 + pump: ^3.0.0 + tar-stream: ^2.1.4 + checksum: f5b9a70059f5b2969e65f037b4e4da2daf0fa762d3d232ffd96e819e3f94665dbbbe62f76f084f1acb4dbdcce16c6e4dac08d12ffc6d24b8d76720f4d9cf032d + languageName: node + linkType: hard + +"tar-fs@npm:^3.0.5": version: 3.0.5 resolution: "tar-fs@npm:3.0.5" dependencies: @@ -39280,18 +39485,6 @@ __metadata: languageName: node linkType: hard -"tar-fs@npm:^2.0.0": - version: 2.1.1 - resolution: "tar-fs@npm:2.1.1" - dependencies: - chownr: ^1.1.1 - mkdirp-classic: ^0.5.2 - pump: ^3.0.0 - tar-stream: ^2.1.4 - checksum: f5b9a70059f5b2969e65f037b4e4da2daf0fa762d3d232ffd96e819e3f94665dbbbe62f76f084f1acb4dbdcce16c6e4dac08d12ffc6d24b8d76720f4d9cf032d - languageName: node - linkType: hard - "tar-fs@npm:~2.0.1": version: 2.0.1 resolution: "tar-fs@npm:2.0.1" @@ -39559,7 +39752,7 @@ __metadata: languageName: node linkType: hard -"through@npm:2, through@npm:^2.3.6, through@npm:^2.3.8": +"through@npm:2, through@npm:^2.3.6": version: 2.3.8 resolution: "through@npm:2.3.8" checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd @@ -40416,16 +40609,6 @@ __metadata: languageName: node linkType: hard -"unbzip2-stream@npm:1.4.3": - version: 1.4.3 - resolution: "unbzip2-stream@npm:1.4.3" - dependencies: - buffer: ^5.2.1 - through: ^2.3.8 - checksum: 0e67c4a91f4fa0fc7b4045f8b914d3498c2fc2e8c39c359977708ec85ac6d6029840e97f508675fdbdf21fcb8d276ca502043406f3682b70f075e69aae626d1d - languageName: node - linkType: hard - "undefsafe@npm:^2.0.5": version: 2.0.5 resolution: "undefsafe@npm:2.0.5" @@ -40823,13 +41006,6 @@ __metadata: languageName: node linkType: hard -"urlpattern-polyfill@npm:10.0.0": - version: 10.0.0 - resolution: "urlpattern-polyfill@npm:10.0.0" - checksum: 61d890f151ea4ecf34a3dcab32c65ad1f3cda857c9d154af198260c6e5b2ad96d024593409baaa6d4428dd1ab206c14799bf37fe011117ac93a6a44913ac5aa4 - languageName: node - linkType: hard - "urlpattern-polyfill@npm:^9.0.0": version: 9.0.0 resolution: "urlpattern-polyfill@npm:9.0.0" @@ -41948,7 +42124,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:*, ws@npm:8.16.0, ws@npm:^8.11.0, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.14.2, ws@npm:^8.16.0, ws@npm:^8.8.0": +"ws@npm:*, ws@npm:^8.11.0, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.14.2, ws@npm:^8.16.0, ws@npm:^8.8.0": version: 8.16.0 resolution: "ws@npm:8.16.0" peerDependencies: @@ -42239,21 +42415,6 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.7.2, yargs@npm:^17.1.1, yargs@npm:^17.3.1, yargs@npm:^17.7.1, yargs@npm:^17.7.2": - version: 17.7.2 - resolution: "yargs@npm:17.7.2" - dependencies: - cliui: ^8.0.1 - escalade: ^3.1.1 - get-caller-file: ^2.0.5 - require-directory: ^2.1.1 - string-width: ^4.2.3 - y18n: ^5.0.5 - yargs-parser: ^21.1.1 - checksum: 73b572e863aa4a8cbef323dd911d79d193b772defd5a51aab0aca2d446655216f5002c42c5306033968193bdbf892a7a4c110b0d77954a7fdf563e653967b56a - languageName: node - linkType: hard - "yargs@npm:^15.1.0": version: 15.4.1 resolution: "yargs@npm:15.4.1" @@ -42288,23 +42449,28 @@ __metadata: languageName: node linkType: hard -"yauzl@npm:^2.10.0": - version: 2.10.0 - resolution: "yauzl@npm:2.10.0" +"yargs@npm:^17.1.1, yargs@npm:^17.3.1, yargs@npm:^17.7.1, yargs@npm:^17.7.2": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" dependencies: - buffer-crc32: ~0.2.3 - fd-slicer: ~1.1.0 - checksum: 7f21fe0bbad6e2cb130044a5d1d0d5a0e5bf3d8d4f8c4e6ee12163ce798fee3de7388d22a7a0907f563ac5f9d40f8699a223d3d5c1718da90b0156da6904022b + cliui: ^8.0.1 + escalade: ^3.1.1 + get-caller-file: ^2.0.5 + require-directory: ^2.1.1 + string-width: ^4.2.3 + y18n: ^5.0.5 + yargs-parser: ^21.1.1 + checksum: 73b572e863aa4a8cbef323dd911d79d193b772defd5a51aab0aca2d446655216f5002c42c5306033968193bdbf892a7a4c110b0d77954a7fdf563e653967b56a languageName: node linkType: hard "yauzl@npm:^3.0.0": - version: 3.1.2 - resolution: "yauzl@npm:3.1.2" + version: 3.1.3 + resolution: "yauzl@npm:3.1.3" dependencies: buffer-crc32: ~0.2.3 pend: ~1.2.0 - checksum: 8ed80d4b2dfe749498b77120089bd216f50bbcd463a7a719e84cd92d1ba9ffc64eb90f3a8590f93521fccfc6dad5c757e4cef9aef3d78d94890aab2194450005 + checksum: 5b782f6e99361a9c715e7a82e7aae3d983b6ddff6ebe3a66d2dd3f4ee601ec41c55fa88587bf6de0acbc013aac0b2cac84f9f0cd48372fd5329ee5e273f46f2c languageName: node linkType: hard @@ -42469,7 +42635,7 @@ __metadata: languageName: node linkType: hard -"zod@npm:3.22.4, zod@npm:^3.22.4": +"zod@npm:^3.22.4": version: 3.22.4 resolution: "zod@npm:3.22.4" checksum: 80bfd7f8039b24fddeb0718a2ec7c02aa9856e4838d6aa4864335a047b6b37a3273b191ef335bf0b2002e5c514ef261ffcda5a589fb084a48c336ffc4cdbab7f