diff --git a/.changeset/beige-chairs-suffer.md b/.changeset/beige-chairs-suffer.md new file mode 100644 index 0000000000..c642b70cbb --- /dev/null +++ b/.changeset/beige-chairs-suffer.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': minor +--- + +`SimpleStepper` back button now works with `activeStep` property set higher than 0 diff --git a/.changeset/blue-camels-fry.md b/.changeset/blue-camels-fry.md new file mode 100644 index 0000000000..1f24a62b5b --- /dev/null +++ b/.changeset/blue-camels-fry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-module-techdocs': patch +--- + +Added an extension point that allows for custom entity filtering during document collation. diff --git a/.changeset/breezy-socks-cross.md b/.changeset/breezy-socks-cross.md new file mode 100644 index 0000000000..41bc7f9cdc --- /dev/null +++ b/.changeset/breezy-socks-cross.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +--- + +fix: Creating a repository in a user namespace would always lead to an error diff --git a/.changeset/brown-dots-shake.md b/.changeset/brown-dots-shake.md new file mode 100644 index 0000000000..821df15c82 --- /dev/null +++ b/.changeset/brown-dots-shake.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-plugin-api': patch +--- + +Failure to lazy load an extension will now always result in an error being thrown to be forwarded to error boundaries, rather than being rendered using the `BootErrorPage` app component. diff --git a/.changeset/chubby-kids-hear.md b/.changeset/chubby-kids-hear.md new file mode 100644 index 0000000000..b6461e8f71 --- /dev/null +++ b/.changeset/chubby-kids-hear.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Fix Button types that was preventing the use of native attributes like onClick. diff --git a/.changeset/cyan-needles-hear.md b/.changeset/cyan-needles-hear.md new file mode 100644 index 0000000000..0decabf45b --- /dev/null +++ b/.changeset/cyan-needles-hear.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +The starred entities component uses the entity title or display name if it exists diff --git a/.changeset/dull-olives-itch.md b/.changeset/dull-olives-itch.md new file mode 100644 index 0000000000..679ed584d7 --- /dev/null +++ b/.changeset/dull-olives-itch.md @@ -0,0 +1,72 @@ +--- +'@backstage/plugin-scaffolder-node': minor +--- + +**DEPRECATION**: We've deprecated the old way of defining actions using `createTemplateAction` with raw `JSONSchema` and type parameters, as well as using `zod` through an import. You can now use the new format to define `createTemplateActions` with `zod` provided by the framework. This change also removes support for `logStream` in the `context` as well as moving the `logger` to an instance of `LoggerService`. + +Before: + +```ts +createTemplateAction<{ repoUrl: string }, { test: string }>({ + id: 'test', + schema: { + input: { + type: 'object', + required: ['repoUrl'], + properties: { + repoUrl: { type: 'string' }, + }, + }, + output: { + type: 'object', + required: ['test'], + properties: { + test: { type: 'string' }, + }, + }, + }, + handler: async ctx => { + ctx.logStream.write('blob'); + }, +}); + +// or + +createTemplateAction({ + id: 'test', + schema: { + input: z.object({ + repoUrl: z.string(), + }), + output: z.object({ + test: z.string(), + }), + }, + handler: async ctx => { + ctx.logStream.write('something'); + }, +}); +``` + +After: + +```ts +createTemplateAction({ + id: 'test', + schema: { + input: { + repoUrl: d => d.string(), + }, + output: { + test: d => d.string(), + }, + }, + handler: async ctx => { + // you can just use ctx.logger.log('...'), or if you really need a log stream you can do this: + const logStream = new PassThrough(); + logStream.on('data', chunk => { + ctx.logger.info(chunk.toString()); + }); + }, +}); +``` diff --git a/.changeset/early-buses-lick.md b/.changeset/early-buses-lick.md new file mode 100644 index 0000000000..6b3650d3b4 --- /dev/null +++ b/.changeset/early-buses-lick.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': minor +--- + +The default auth injection middleware for the `FetchApi` will now also take configuration under `discovery.endpoints` into consideration when deciding whether to include credentials or not. diff --git a/.changeset/eight-jeans-shop.md b/.changeset/eight-jeans-shop.md new file mode 100644 index 0000000000..004e75038f --- /dev/null +++ b/.changeset/eight-jeans-shop.md @@ -0,0 +1,7 @@ +--- +'@backstage/frontend-plugin-api': minor +'@backstage/frontend-defaults': minor +'@backstage/frontend-app-api': patch +--- + +Introduced a `createFrontendFeatureLoader()` function, as well as a `FrontendFeatureLoader` interface, to gather several frontend plugins, modules or feature loaders in a single exported entrypoint and load them, possibly asynchronously. This new feature, very similar to the `createBackendFeatureLoader()` already available on the backend, supersedes the previous `CreateAppFeatureLoader` type which has been deprecated. diff --git a/.changeset/first-purple-bykors.md b/.changeset/first-purple-bykors.md new file mode 100644 index 0000000000..e5b6e171d0 --- /dev/null +++ b/.changeset/first-purple-bykors.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-permission-node': minor +--- + +**BREAKING** The `ServerPermissionClient` can no longer be instantiated with a `tokenManager` and must instead be instantiated with an `auth` service. If you are still on the legacy backend system, use `createLegacyAuthAdapters()` from `@backstage/backend-common` to create a compatible `auth` service. diff --git a/.changeset/gentle-lemons-explain.md b/.changeset/gentle-lemons-explain.md new file mode 100644 index 0000000000..435ca4003b --- /dev/null +++ b/.changeset/gentle-lemons-explain.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-test-utils': patch +--- + +Added a `initialRouteEntries` option to `renderInTestApp`. diff --git a/.changeset/gentle-students-glow.md b/.changeset/gentle-students-glow.md new file mode 100644 index 0000000000..ade77cbb95 --- /dev/null +++ b/.changeset/gentle-students-glow.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-test-utils': patch +--- + +The `renderInTestApp` helper now provides a default mock config with mock values for both `app.baseUrl` and `backend.baseUrl`. diff --git a/.changeset/giant-phones-melt.md b/.changeset/giant-phones-melt.md new file mode 100644 index 0000000000..1506e2d0f3 --- /dev/null +++ b/.changeset/giant-phones-melt.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-test-utils': minor +--- + +**BREAKING**: Removed deprecated `setupRequestMockHandlers` which was replaced by `registerMswTestHooks`. diff --git a/.changeset/good-chairs-visit.md b/.changeset/good-chairs-visit.md new file mode 100644 index 0000000000..15aa04a855 --- /dev/null +++ b/.changeset/good-chairs-visit.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +--- + +Made gitlab:repo:push action idempotent. diff --git a/.changeset/gorgeous-keys-shop.md b/.changeset/gorgeous-keys-shop.md new file mode 100644 index 0000000000..f319f0bdbf --- /dev/null +++ b/.changeset/gorgeous-keys-shop.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-node-test-utils': minor +--- + +Use update `createTemplateAction` kinds diff --git a/.changeset/honest-ravens-stare.md b/.changeset/honest-ravens-stare.md new file mode 100644 index 0000000000..7b78ce4357 --- /dev/null +++ b/.changeset/honest-ravens-stare.md @@ -0,0 +1,7 @@ +--- +'@backstage/frontend-defaults': minor +'@backstage/frontend-app-api': minor +'@backstage/core-compat-api': minor +--- + +**BREAKING**: Dropped support for the removed opaque `@backstage/ExtensionOverrides` and `@backstage/BackstagePlugin` types. diff --git a/.changeset/itchy-shoes-rest.md b/.changeset/itchy-shoes-rest.md new file mode 100644 index 0000000000..3e40fab36e --- /dev/null +++ b/.changeset/itchy-shoes-rest.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': minor +--- + +**BREAKING**: Removed the deprecated `ExtensionOverrides` and `FrontendFeature` types. diff --git a/.changeset/lazy-deers-dance.md b/.changeset/lazy-deers-dance.md new file mode 100644 index 0000000000..80adcbaa14 --- /dev/null +++ b/.changeset/lazy-deers-dance.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': minor +--- + +**BREAKING**: Removed deprecated variant of `createExtensionDataRef` where the ID is passed directly. diff --git a/.changeset/long-apples-jog.md b/.changeset/long-apples-jog.md new file mode 100644 index 0000000000..4f57576cc4 --- /dev/null +++ b/.changeset/long-apples-jog.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-test-utils': patch +--- + +Added support for PostgreSQL version 17 diff --git a/.changeset/metal-crabs-agree.md b/.changeset/metal-crabs-agree.md new file mode 100644 index 0000000000..c5a2d1102d --- /dev/null +++ b/.changeset/metal-crabs-agree.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': minor +--- + +Declared CancelIcon explicitly on Chip component inside Select.tsx to disable onMouseDown event by default that creates the flaw of re-opening select component when user tries to remove a selected filter. diff --git a/.changeset/neat-roses-melt.md b/.changeset/neat-roses-melt.md new file mode 100644 index 0000000000..e0423ee0b9 --- /dev/null +++ b/.changeset/neat-roses-melt.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search': patch +--- + +Expand the default kind filter to include all kinds from the System Model. diff --git a/.changeset/odd-boxes-pump.md b/.changeset/odd-boxes-pump.md new file mode 100644 index 0000000000..b0629fa428 --- /dev/null +++ b/.changeset/odd-boxes-pump.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-compat-api': patch +--- + +Improved route path normalization when converting existing route elements in `converLegacyApp`, for example handling trailing `/*` in paths. diff --git a/.changeset/olive-dragons-guess.md b/.changeset/olive-dragons-guess.md new file mode 100644 index 0000000000..c1e3fa6286 --- /dev/null +++ b/.changeset/olive-dragons-guess.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +Support new `createTemplateAction` type, and convert `catalog:fetch` action to new way of defining actions. diff --git a/.changeset/poor-bats-retire.md b/.changeset/poor-bats-retire.md new file mode 100644 index 0000000000..8b7d272299 --- /dev/null +++ b/.changeset/poor-bats-retire.md @@ -0,0 +1,32 @@ +--- +'@backstage/app-defaults': minor +--- + +**BREAKING**: The default `DiscoveryApi` implementation has been switched to use `FrontendHostDiscovery`, which adds support for the `discovery.endpoints` configuration. + +This is marked as a breaking change because it will cause any existing `discovery.endpoints` configuration to be picked up and used, which may break existing setups. + +For example, consider the following configuration: + +```yaml +app: + baseUrl: https://backstage.acme.org + +backend: + baseUrl: https://backstage.internal.acme.org + +discovery: + endpoints: + - target: https://catalog.internal.acme.org/api/{{pluginId}} + plugins: [catalog] +``` + +This will now cause requests from the frontend towards the `catalog` plugin to be routed to `https://catalog.internal.acme.org/api/catalog`, but this might not be reachable from the frontend. To fix this, you should update the `discovery.endpoints` configuration to only override the internal URL of the plugin: + +```yaml +discovery: + endpoints: + - target: + internal: https://catalog.internal.acme.org/api/{{pluginId}} + plugins: [catalog] +``` diff --git a/.changeset/popular-zebras-reply.md b/.changeset/popular-zebras-reply.md new file mode 100644 index 0000000000..80d6da993b --- /dev/null +++ b/.changeset/popular-zebras-reply.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Internal change to migrate `lint` to the new module system. diff --git a/.changeset/pre.json b/.changeset/pre.json index 198843d089..2add7df8c1 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -198,67 +198,122 @@ }, "changesets": [ "angry-plants-peel", + "brave-ears-bow", + "brave-wasps-fry", + "breezy-socks-cross", "bright-pets-agree", "calm-bottles-rest", "calm-files-hunt", + "catch-you-mamma", "chatty-turkeys-brake", "chilled-bugs-draw", + "chilled-rocks-rule", "cool-moons-lay", "create-app-1740489353", "create-app-1741106897", + "cuddly-apricots-fetch", + "dependabot-64f57a3", "dirty-turkeys-remain", "dull-coats-help", + "dull-olives-itch", + "early-buses-lick", + "fair-pianos-add", "fair-rabbits-greet", "famous-ladybugs-swim", "famous-wombats-explain", "fluffy-ghosts-drop", + "four-badgers-buy", "four-readers-vanish", "friendly-garlics-explain", + "gentle-lemons-explain", + "gentle-students-glow", + "giant-phones-melt", "gold-donkeys-flow", "good-lions-tease", + "gorgeous-keys-shop", + "grumpy-humans-brush", + "hip-ladybugs-behave", + "honest-ravens-stare", "hungry-walls-pump", "itchy-houses-whisper", "itchy-moose-raise", "itchy-schools-camp", + "itchy-shoes-rest", "kind-paws-visit", + "kind-waves-impress", "late-hairs-kneel", "lazy-apricots-whisper", + "lazy-deers-dance", "light-turtles-talk", + "loud-clocks-obey", + "lucky-ducks-attend", + "many-insects-add", + "many-rockets-marry", + "many-wolves-own", + "mira-color-like", "nasty-lemons-scream", + "nervous-cups-happen", + "nice-dolphins-own", "nice-ghosts-clean", "nine-hounds-rest", "ninety-experts-cheat", "ninety-teachers-tease", + "old-avocados-grow", + "old-ladybugs-report", "olive-carrots-move", "olive-dodos-applaud", + "olive-dragons-guess", "olive-planes-pay", + "poor-bats-retire", + "poor-phones-switch", "popular-bobcats-occur", "pretty-chicken-eat", + "quick-schools-grin", "real-hounds-mate", + "real-turkeys-hunt", "red-hotels-destroy", + "renovate-e8e0621", + "rich-berries-glow", "rich-sloths-hang", + "rotten-bees-design", "selfish-cheetahs-sip", + "shiny-ways-develop", "short-moose-attend", "shy-geckos-leave", + "shy-knives-matter", "silly-otters-hang", "silly-taxis-suffer", + "silver-camels-brake", "silver-fishes-shop", "six-shrimps-breathe", + "six-taxis-film", "six-teachers-prove", + "sixty-ducks-bathe", + "slimy-rockets-jog", + "small-onions-live", + "smart-pens-change", + "stale-frogs-wonder", "strange-eels-turn", "strange-masks-type", "sweet-zoos-beam", + "swift-rings-switch", "swift-worms-behave", "tall-falcons-juggle", + "tall-meals-prove", "techdocs-four-needles-switch", "techdocs-late-ants-remain", "techdocs-slow-zebras-camp", + "ten-impalas-hope", + "thick-deers-destroy", + "thin-books-rest", "thin-experts-sing", "thin-steaks-trade", "three-bears-heal", "tidy-ads-cover", "tiny-llamas-boil", + "unlucky-cups-sell", "weak-lemons-sing", + "wicked-dancers-count", "young-gifts-know" ] } diff --git a/.changeset/real-turkeys-hunt.md b/.changeset/real-turkeys-hunt.md new file mode 100644 index 0000000000..ba8fe44518 --- /dev/null +++ b/.changeset/real-turkeys-hunt.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-scaffolder-react': patch +--- + +Disable the submit button on creating diff --git a/.changeset/renovate-e8e0621.md b/.changeset/renovate-e8e0621.md new file mode 100644 index 0000000000..67d1157ff5 --- /dev/null +++ b/.changeset/renovate-e8e0621.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': patch +--- + +Updated dependency `flatted` to `3.3.3`. diff --git a/.changeset/rich-berries-glow.md b/.changeset/rich-berries-glow.md new file mode 100644 index 0000000000..07312591a3 --- /dev/null +++ b/.changeset/rich-berries-glow.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-app-api': minor +'@backstage/backend-defaults': patch +--- + +The `discovery.endpoints` configuration no longer requires both `internal` and `external` target when using the object form, instead falling back to the default. diff --git a/.changeset/serious-chefs-provide.md b/.changeset/serious-chefs-provide.md new file mode 100644 index 0000000000..30d9adf15c --- /dev/null +++ b/.changeset/serious-chefs-provide.md @@ -0,0 +1,7 @@ +--- +'@backstage/core-components': patch +--- + +Avoid Layout Shift for `DismissableBanner` when using a `storageApi` with latency (e.g. `user-settings-backend`) + +Properly handle the `unknown` state of the `storageApi`. There's a trade-off: this may lead to some Layout Shift if the banner has not been dismissed, but once it has been dismissed, you won't have any. diff --git a/.changeset/silver-camels-brake.md b/.changeset/silver-camels-brake.md new file mode 100644 index 0000000000..f044bc9441 --- /dev/null +++ b/.changeset/silver-camels-brake.md @@ -0,0 +1,14 @@ +--- +'@backstage/plugin-scaffolder-backend-module-bitbucket-server': patch +'@backstage/plugin-scaffolder-backend-module-bitbucket-cloud': patch +'@backstage/plugin-scaffolder-backend-module-bitbucket': patch +'@backstage/plugin-scaffolder-backend-module-gerrit': patch +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +'@backstage/plugin-scaffolder-backend-module-azure': patch +'@backstage/plugin-scaffolder-backend-module-gitea': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-scaffolder-node': patch +'@backstage/integration': patch +--- + +Allow signing git commits using configured private PGP key in scaffolder diff --git a/.changeset/six-taxis-film.md b/.changeset/six-taxis-film.md new file mode 100644 index 0000000000..38f2b33fe5 --- /dev/null +++ b/.changeset/six-taxis-film.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-compat-api': patch +--- + +Added the `entityPage` option to `convertLegacyApp`, which you can read more about in the [app migration docs](https://backstage.io/docs/frontend-system/building-apps/migrating#entity-pages). diff --git a/.changeset/slimy-rockets-jog.md b/.changeset/slimy-rockets-jog.md new file mode 100644 index 0000000000..09cdae53d0 --- /dev/null +++ b/.changeset/slimy-rockets-jog.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-bitbucket-cloud': patch +--- + +Fixing spelling mistake in `jsonschema` diff --git a/.changeset/small-onions-live.md b/.changeset/small-onions-live.md new file mode 100644 index 0000000000..fc2ed28fe9 --- /dev/null +++ b/.changeset/small-onions-live.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-auth-backend-module-oauth2-provider': patch +'@backstage/plugin-auth-backend-module-oidc-provider': patch +'@backstage/plugin-auth-backend-module-okta-provider': patch +--- + +Fixed repository url in `README.md` diff --git a/.changeset/smooth-rooms-nail.md b/.changeset/smooth-rooms-nail.md new file mode 100644 index 0000000000..1a193b802c --- /dev/null +++ b/.changeset/smooth-rooms-nail.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': minor +--- + +Updating styles for Text and Link components as well as global surface tokens. diff --git a/.changeset/ten-impalas-hope.md b/.changeset/ten-impalas-hope.md new file mode 100644 index 0000000000..5bbdea7a11 --- /dev/null +++ b/.changeset/ten-impalas-hope.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-events-backend': patch +'@backstage/plugin-events-node': patch +--- + +add `addHttpPostBodyParser` to events extension to allow body parse customization. This feature will enhance flexibility in handling HTTP POST requests in event-related operations. diff --git a/.changeset/wicked-dancers-count.md b/.changeset/wicked-dancers-count.md new file mode 100644 index 0000000000..24d3e95a05 --- /dev/null +++ b/.changeset/wicked-dancers-count.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Fixed bug in fs:delete causing no files to be deleted on windows machines diff --git a/.github/workflows/api-breaking-changes-comment.yml b/.github/workflows/api-breaking-changes-comment.yml index 1256d294fa..91c0a43447 100644 --- a/.github/workflows/api-breaking-changes-comment.yml +++ b/.github/workflows/api-breaking-changes-comment.yml @@ -74,7 +74,7 @@ jobs: - name: Cache Comment if: ${{ steps.event.outputs.ACTION != 'closed' }} - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 with: path: comment.md key: ${{ steps.hash.outputs.COMMENT_FILE_HASH }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 715d26d5e4..d1b5df56ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -161,8 +161,8 @@ jobs: name: Test ${{ matrix.node-version }} services: - postgres16: - image: postgres:16 + postgres17: + image: postgres:17 env: POSTGRES_PASSWORD: postgres options: >- @@ -172,8 +172,8 @@ jobs: --health-retries 5 ports: - 5432/tcp - postgres12: - image: postgres:12 + postgres13: + image: postgres:13 env: POSTGRES_PASSWORD: postgres options: >- @@ -253,8 +253,8 @@ jobs: run: yarn backstage-cli repo test --maxWorkers=3 --workerIdleMemoryLimit=1300M --since origin/master --successCache --successCacheDir .cache/backstage-cli env: BACKSTAGE_TEST_DISABLE_DOCKER: 1 - BACKSTAGE_TEST_DATABASE_POSTGRES16_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres16.ports[5432] }} - BACKSTAGE_TEST_DATABASE_POSTGRES12_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres12.ports[5432] }} + BACKSTAGE_TEST_DATABASE_POSTGRES17_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres17.ports[5432] }} + BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres13.ports[5432] }} BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING: mysql://root:root@localhost:${{ job.services.mysql8.ports[3306] }}/ignored BACKSTAGE_TEST_CACHE_REDIS7_CONNECTION_STRING: redis://localhost:${{ job.services.redis.ports[6379] }} diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index ebcf5b5c72..0387de0d97 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -16,8 +16,8 @@ jobs: node-version: [20.x, 22.x] services: - postgres16: - image: postgres:16 + postgres17: + image: postgres:17 env: POSTGRES_PASSWORD: postgres options: >- @@ -27,8 +27,8 @@ jobs: --health-retries 5 ports: - 5432/tcp - postgres12: - image: postgres:12 + postgres13: + image: postgres:13 env: POSTGRES_PASSWORD: postgres options: >- @@ -122,8 +122,8 @@ jobs: yarn backstage-cli repo test --maxWorkers=3 --workerIdleMemoryLimit=1300M --coverage --successCache --successCacheDir .cache/backstage-cli env: BACKSTAGE_TEST_DISABLE_DOCKER: 1 - BACKSTAGE_TEST_DATABASE_POSTGRES16_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres16.ports[5432] }} - BACKSTAGE_TEST_DATABASE_POSTGRES12_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres12.ports[5432] }} + BACKSTAGE_TEST_DATABASE_POSTGRES17_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres17.ports[5432] }} + BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres13.ports[5432] }} BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING: mysql://root:root@localhost:${{ job.services.mysql8.ports[3306] }}/ignored BACKSTAGE_TEST_CACHE_REDIS7_CONNECTION_STRING: redis://localhost:${{ job.services.redis.ports[6379] }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index bc0cff1182..4a5707087e 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/upload-sarif@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11 with: sarif_file: results.sarif diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index c08e71cbcb..aec03d09ee 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -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@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/upload-sarif@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11 with: sarif_file: snyk.sarif diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml index ac4c3c7e81..d028be98d8 100644 --- a/.github/workflows/verify_codeql.yml +++ b/.github/workflows/verify_codeql.yml @@ -55,7 +55,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/init@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11 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@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/autobuild@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11 # ℹ️ 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@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/analyze@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11 diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml index 8f2447ec26..7ae7e298e7 100644 --- a/.github/workflows/verify_e2e-linux.yml +++ b/.github/workflows/verify_e2e-linux.yml @@ -21,7 +21,7 @@ jobs: services: postgres: - image: postgres:12 + image: postgres:13 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres diff --git a/OWNERS.md b/OWNERS.md index 6eb42bb36d..8190e31bfd 100644 --- a/OWNERS.md +++ b/OWNERS.md @@ -132,7 +132,6 @@ Scope: Tooling and Community Repo Maintainers for the Backstage [Community Plugi | André Wanlin | Spotify | [awanlin](https://github.com/awanlin) | `ahhhndre` | | Bethany Griggs | Red Hat | [BethGriggs](https://github.com/BethGriggs) | `bethgriggs` | | Kashish Mittal | Red Hat | [04kash](https://github.com/04kash) | `kashh._.` | -| Nick Boldt | Red Hat | [nickboldt](https://github.com/nickboldt) | `nboldt` | | Vincenzo Scamporlino | Spotify | [vinzscam](https://github.com/vinzscam) | `vinzscam` | ### Events diff --git a/README-fr_FR.md b/README-fr_FR.md index e0cffaa5c5..a54e9c8c49 100644 --- a/README-fr_FR.md +++ b/README-fr_FR.md @@ -66,7 +66,7 @@ Si vous voulez contribuer et vous impliquer dans notre communauté, voici les re ## Licence -Copyright 2020-2024 © Les auteurs de Backstage. Tous droits réservés. La Linux Foundation détient des marques déposées et utilise des marques commerciales. Pour une liste des marques de commerce de la Linux Foundation, veuillez consulter notre page d'utilisation des marques: https://www.linuxfoundation.org/trademark-usage +Copyright 2020-2025 © Les auteurs de Backstage. Tous droits réservés. La Linux Foundation détient des marques déposées et utilise des marques commerciales. Pour une liste des marques de commerce de la Linux Foundation, veuillez consulter notre page d'utilisation des marques: https://www.linuxfoundation.org/trademark-usage Sous licence Apache, version 2.0: http://www.apache.org/licenses/LICENSE-2.0 diff --git a/README-ko_kr.md b/README-ko_kr.md index b2bba41567..e43aa60c43 100644 --- a/README-ko_kr.md +++ b/README-ko_kr.md @@ -65,7 +65,7 @@ Backstage의 문서는 다음을 포함합니다: ## License -Copyright 2020-2024 © The Backstage 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 +Copyright 2020-2025 © The Backstage 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 Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 diff --git a/README-zh_Hans.md b/README-zh_Hans.md index 129c7e7aa4..4661564725 100644 --- a/README-zh_Hans.md +++ b/README-zh_Hans.md @@ -65,7 +65,7 @@ Backstage 的文档包括: ## 许可 -版权所有 2020-2024 © Backstage 作者。版权所有。Linux 基金会已注册商标并使用商标。有关 Linux 基金会的商标列表,请参阅我们的商标使用页面:https://www.linuxfoundation.org/trademark-usage +版权所有 2020-2025 © Backstage 作者。版权所有。Linux 基金会已注册商标并使用商标。有关 Linux 基金会的商标列表,请参阅我们的商标使用页面:https://www.linuxfoundation.org/trademark-usage 采用 Apache v2.0 许可:http://www.apache.org/licenses/LICENSE-2.0 diff --git a/README.md b/README.md index 64a7b34b64..e3e9bd77dd 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ See the [GOVERNANCE.md](https://github.com/backstage/community/blob/main/GOVERNA ## License -Copyright 2020-2024 © The Backstage 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 +Copyright 2020-2025 © The Backstage 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 Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 diff --git a/beps/0004-scaffolder-task-idempotency/README.md b/beps/0004-scaffolder-task-idempotency/README.md index 208446158e..e23cc519d1 100644 --- a/beps/0004-scaffolder-task-idempotency/README.md +++ b/beps/0004-scaffolder-task-idempotency/README.md @@ -154,30 +154,36 @@ export function createGithubRepoCreateAction(options: { username: owner, }); - await ctx.checkpoint('repo.creation', async () => { - const repoCreationPromise = - user.data.type === 'Organization' - ? client.rest.repos.createInOrg({ - name: repo, - org: owner, - }) - : client.rest.repos.createForAuthenticatedUser({ - name: repo, - }); - const { repoUrl } = await repoCreationPromise; - return { repoUrl }; + await ctx.checkpoint({ + key: 'repo.creation.v1', + fn: async () => { + const repoCreationPromise = + user.data.type === 'Organization' + ? client.rest.repos.createInOrg({ + name: repo, + org: owner, + }) + : client.rest.repos.createForAuthenticatedUser({ + name: repo, + }); + const { repoUrl } = await repoCreationPromise; + return { repoUrl }; + }, }); if (secrets) { - await ctx.checkpoint('repo.create.variables', async () => { - for (const [key, value] of Object.entries(repoVariables ?? {})) { - await client.rest.actions.createRepoVariable({ - owner, - repo, - name: key, - value: value, - }); - } + await ctx.checkpoint({ + key: 'repo.create.variables', + fn: async () => { + for (const [key, value] of Object.entries(repoVariables ?? {})) { + await client.rest.actions.createRepoVariable({ + owner, + repo, + name: key, + value: value, + }); + } + }, }); } @@ -202,9 +208,12 @@ Checkpoints will allow action authors to create actions where code paths are ign This will be provided on a context object and action of author provide a key and a callback. ```typescript -await ctx.checkpoint('repo.creation', async () => { - const { repoUrl } = await client.rest.Repository.create({}); - return { repoUrl }; +await ctx.checkpoint({ + key: 'repo.creation', + fn: async () => { + const { repoUrl } = await client.rest.Repository.create({}); + return { repoUrl }; + }, }); ``` diff --git a/canon-docs/src/app/(docs)/page.mdx b/canon-docs/src/app/(docs)/page.mdx index b2abdefb12..862127ec7b 100644 --- a/canon-docs/src/app/(docs)/page.mdx +++ b/canon-docs/src/app/(docs)/page.mdx @@ -24,8 +24,7 @@ Install Canon using a package manager. Import the global CSS file at the root of your application. ```tsx -import '@backstage/canon/css/core.css'; -import '@backstage/canon/css/components.css'; +import '@backstage/canon/css/styles.css'; ``` ### 3. Start building ✨ diff --git a/canon-docs/src/app/(docs)/theme/theming/page.mdx b/canon-docs/src/app/(docs)/theme/theming/page.mdx index 6ecc2cba58..cab9480a14 100644 --- a/canon-docs/src/app/(docs)/theme/theming/page.mdx +++ b/canon-docs/src/app/(docs)/theme/theming/page.mdx @@ -53,7 +53,7 @@ color of your app. - --canon-bg-elevated + --canon-bg-surface-1 Use for any panels or elevated surfaces. @@ -189,7 +189,7 @@ are prefixed with `fg` to make it easier to identify. --canon-fg-primary - It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`. + It should be used on top of `--canon-bg-app` or `--canon-bg-surface-1`. @@ -197,7 +197,7 @@ are prefixed with `fg` to make it easier to identify. --canon-fg-secondary - It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`. + It should be used on top of `--canon-bg-app` or `--canon-bg-surface-1`. @@ -205,7 +205,7 @@ are prefixed with `fg` to make it easier to identify. --canon-fg-link - It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`. + It should be used on top of `--canon-bg-app` or `--canon-bg-surface-1`. @@ -213,7 +213,7 @@ are prefixed with `fg` to make it easier to identify. --canon-fg-link-hover - It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`. + It should be used on top of `--canon-bg-app` or `--canon-bg-surface-1`. @@ -267,7 +267,7 @@ low contrast to help as a separator with the different background colors. --canon-border - It should be used on top of `--canon-bg-elevated`. + It should be used on top of `--canon-bg-surface-1`. diff --git a/canon-docs/src/components/CustomTheme/customTheme.tsx b/canon-docs/src/components/CustomTheme/customTheme.tsx index 174a8d0b24..e8628b83ff 100644 --- a/canon-docs/src/components/CustomTheme/customTheme.tsx +++ b/canon-docs/src/components/CustomTheme/customTheme.tsx @@ -17,7 +17,7 @@ const defaultTheme = `:root { const myTheme = createTheme({ theme: 'light', settings: { - background: 'var(--canon-bg-elevated)', + background: 'var(--canon-bg-surface-1)', backgroundImage: '', foreground: '#6182B8', caret: '#5d00ff', diff --git a/canon-docs/src/components/Tabs/parts.tsx b/canon-docs/src/components/Tabs/parts.tsx index 7f074f9188..3d02683cef 100644 --- a/canon-docs/src/components/Tabs/parts.tsx +++ b/canon-docs/src/components/Tabs/parts.tsx @@ -31,10 +31,7 @@ export const Tab = (props: React.ComponentProps) => ( {children} diff --git a/canon-docs/src/snippets/_snippets.ts b/canon-docs/src/snippets/_snippets.ts index ad777c48cf..75ff5056f4 100644 --- a/canon-docs/src/snippets/_snippets.ts +++ b/canon-docs/src/snippets/_snippets.ts @@ -5,7 +5,7 @@ export const customTheme = `:root { --canon-font-weight-regular: 400; --canon-font-weight-bold: 600; --canon-bg: #f8f8f8; - --canon-bg-elevated: #fff; + --canon-bg-surface-1: #fff; /* ... other CSS variables */ /* Add your custom components styles here */ @@ -20,7 +20,7 @@ export const customTheme = `:root { --canon-font-weight-regular: 400; --canon-font-weight-bold: 600; --canon-bg: #f8f8f8; - --canon-bg-elevated: #fff; + --canon-bg-surface-1: #fff; /* ... other CSS variables */ /* Add your custom components styles here */ diff --git a/canon-docs/yarn.lock b/canon-docs/yarn.lock index a300a3f720..858a1106e0 100644 --- a/canon-docs/yarn.lock +++ b/canon-docs/yarn.lock @@ -916,11 +916,11 @@ __metadata: linkType: hard "@types/node@npm:^20": - version: 20.17.23 - resolution: "@types/node@npm:20.17.23" + version: 20.17.24 + resolution: "@types/node@npm:20.17.24" dependencies: undici-types: ~6.19.2 - checksum: 9957916d0781f6efd59f2db5bff8be51e0ea0de96f64aa282c0ed7482668b5bc0ad041ea772265ece0e57e1afb64f56b7a5be5fecb03ba467f7c48753b313d86 + checksum: 05d3ca5d8741d10368edeff01318e4e615a7654b0c6bd05415f8cd0a2f851ac8e04c2cac319442c20fe372d1bdba9dd1f40dda014e97902516f82058b68ef6c4 languageName: node linkType: hard @@ -2576,12 +2576,12 @@ __metadata: languageName: node linkType: hard -"framer-motion@npm:^12.4.10": - version: 12.4.10 - resolution: "framer-motion@npm:12.4.10" +"framer-motion@npm:^12.5.0": + version: 12.5.0 + resolution: "framer-motion@npm:12.5.0" dependencies: - motion-dom: ^12.4.10 - motion-utils: ^12.4.10 + motion-dom: ^12.5.0 + motion-utils: ^12.5.0 tslib: ^2.4.0 peerDependencies: "@emotion/is-prop-valid": "*" @@ -2594,7 +2594,7 @@ __metadata: optional: true react-dom: optional: true - checksum: c5e2e3658627e37b91c0a09d0ef908dddc67028240d7b747845ecfe84bb9cadefcaab715f05e587d1baf029a3766356825aac91f0f062e92f29a0f551e59770f + checksum: aebf78b75000c783d12a8ae2534e62f0a5781490c0cfdf39c051b6dee2c7e11a5b8dd1077cc659e55dc24f582aa5d620c28999f3c17e17d38e49b14c6298d86b languageName: node linkType: hard @@ -4118,27 +4118,27 @@ __metadata: languageName: node linkType: hard -"motion-dom@npm:^12.4.10": - version: 12.4.10 - resolution: "motion-dom@npm:12.4.10" +"motion-dom@npm:^12.5.0": + version: 12.5.0 + resolution: "motion-dom@npm:12.5.0" dependencies: - motion-utils: ^12.4.10 - checksum: 6ecf64e5a41db29b228940a9d832f67f69ce4c4b23bb501ed93bb98acaa30deb6bf486cfb319bdf4f63bef5f45448605cb212ab74a819f93dcfaae9e3aeb6053 + motion-utils: ^12.5.0 + checksum: abe92c72fb09eba9a6071fb77aa2020d1489df31c2291f5e3ff15def091d8b2bacb8a60854c5617f40f99e70178c0407519ca846a57e48051c4f49808e0f5927 languageName: node linkType: hard -"motion-utils@npm:^12.4.10": - version: 12.4.10 - resolution: "motion-utils@npm:12.4.10" - checksum: 86335d4c2332ee4215a5a78371b0b8da6d472834802ac6516a43bc237efa5659dbdebc47b64aefaaeacb779d4e8e5639c79a05099a482fc235ed0c4337c89abc +"motion-utils@npm:^12.5.0": + version: 12.5.0 + resolution: "motion-utils@npm:12.5.0" + checksum: 347169ab97d8b3720b363afad6c9f4e8999490d1d95c70aaa8f83333be1b8b4e11c5f25564a9809fb091160fad2184403222e5b383be1bba004243c8b47497fe languageName: node linkType: hard "motion@npm:^12.4.1": - version: 12.4.10 - resolution: "motion@npm:12.4.10" + version: 12.5.0 + resolution: "motion@npm:12.5.0" dependencies: - framer-motion: ^12.4.10 + framer-motion: ^12.5.0 tslib: ^2.4.0 peerDependencies: "@emotion/is-prop-valid": "*" @@ -4151,7 +4151,7 @@ __metadata: optional: true react-dom: optional: true - checksum: 007ed203beaf4d61c941780c96078c48822ee913a4b34c7232a165592405e1f81cc267b3645061a4bbaed9fe1d2260ecd4e12433b5062c50dc882f469069d8b9 + checksum: a32231ea53a3f3b659630a86b40b5cd61daaa4428db06746c7aa06124c6c27ba2772a6482d652d1b4c71d2d9f01e30602eb700655f32fd1c0ccba74b1695fd8c languageName: node linkType: hard diff --git a/docs/backend-system/building-plugins-and-modules/02-testing.md b/docs/backend-system/building-plugins-and-modules/02-testing.md index 1ec100cff3..439fed597a 100644 --- a/docs/backend-system/building-plugins-and-modules/02-testing.md +++ b/docs/backend-system/building-plugins-and-modules/02-testing.md @@ -190,7 +190,7 @@ describe('MyDatabaseClass', () => { // "physical" databases to test against is much costlier than creating the // "logical" databases within them that the individual tests use. const databases = TestDatabases.create({ - ids: ['POSTGRES_16', 'POSTGRES_12', 'SQLITE_3', 'MYSQL_8'], + ids: ['POSTGRES_17', 'POSTGRES_13', 'SQLITE_3', 'MYSQL_8'], }); // Just an example of how to conveniently bundle up the setup code @@ -236,8 +236,8 @@ your CI environment is able to supply databases natively, the `TestDatabases` support custom connection strings through the use of environment variables that it'll take into account when present. +- `BACKSTAGE_TEST_DATABASE_POSTGRES17_CONNECTION_STRING` - `BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING` -- `BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING` - `BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING` ## Testing Service Factories diff --git a/docs/features/search/collators.md b/docs/features/search/collators.md index 834a14437a..d87258f65b 100644 --- a/docs/features/search/collators.md +++ b/docs/features/search/collators.md @@ -134,6 +134,38 @@ search: timeout: { minutes: 3 } ``` +### Filtering through the catalog collator + +The TechDocs collator by default filters through catalog entities where the annotation `metadata.annotations.backstage.io/techdocs-ref` exists. If you wish to further filter out entities, there are two ways to do so through the `techDocsCollatorEntityFilterExtensionPoint`. + +```typescript +export const exampleCustomCatalogFiltering = createBackendModule({ + pluginId: 'search', + moduleId: 'search-techdocs-collator-entity-filter', + register(reg) { + reg.registerInit({ + deps: { + customCollatorFilter: techDocsCollatorEntityFilterExtensionPoint, + }, + async init({ customCollatorFilter }) { + /* filtering by catalog params */ + customCollatorFilter.setCustomCatalogApiFilters([ + { kind: ['API', 'Component', ...] }, + { metadata: ['...more filters'] }, + ]); + + /* filtering by a custom function */ + customCollatorFilter.setEntityFilterFunction((entities: Entity[]) => + entities.filter( + entity => entity.metadata?.annotations?.abc === 'xyz', + ), + ); + }, + }); + }, +}); +``` + ## Community Collators Here are some of the known Search Collators available in from the Backstage Community: diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md index 0006a52ab2..dd7a5e60a1 100644 --- a/docs/features/software-templates/writing-custom-actions.md +++ b/docs/features/software-templates/writing-custom-actions.md @@ -19,9 +19,11 @@ array when registering your custom actions, as seen below. ## Streamlining Custom Action Creation with Backstage CLI -The creation of custom actions in Backstage has never been easier thanks to the Backstage CLI. This tool streamlines the setup process, allowing you to focus on your actions' unique functionality. +The creation of custom actions in Backstage has never been easier thanks to the Backstage CLI. This tool streamlines the +setup process, allowing you to focus on your actions' unique functionality. -Start by using the `yarn backstage-cli new` command to generate a scaffolder module. This command sets up the necessary boilerplate code, providing a smooth start: +Start by using the `yarn backstage-cli new` command to generate a scaffolder module. This command sets up the necessary +boilerplate code, providing a smooth start: ``` $ yarn backstage-cli new @@ -34,13 +36,19 @@ $ yarn backstage-cli new You can find a [list](../../tooling/cli/03-commands.md) of all commands provided by the Backstage CLI. -When prompted, select the option to generate a scaffolder module. This creates a solid foundation for your custom action. Enter the name of the module you wish to create, and the CLI will generate the required files and directory structure. +When prompted, select the option to generate a scaffolder module. This creates a solid foundation for your custom +action. Enter the name of the module you wish to create, and the CLI will generate the required files and directory +structure. ## Writing your Custom Action -After running the command, the CLI will create a new directory with your new scaffolder module. This directory will be the working directory for creating the custom action. It will contain all the necessary files and boilerplate code to get started. +After running the command, the CLI will create a new directory with your new scaffolder module. This directory will be +the working directory for creating the custom action. It will contain all the necessary files and boilerplate code to +get started. -Let's create a simple action that adds a new file and some contents that are passed as `input` to the function. Within the generated directory, locate the file at `src/actions/example/example.ts`. Feel free to rename this file along with its generated unit test. We will replace the existing placeholder code with our custom action code as follows: +Let's create a simple action that adds a new file and some contents that are passed as `input` to the function. Within +the generated directory, locate the file at `src/actions/example/example.ts`. Feel free to rename this file along with +its generated unit test. We will replace the existing placeholder code with our custom action code as follows: ```ts title="With Zod" import { resolveSafeChildPath } from '@backstage/backend-plugin-api'; @@ -82,7 +90,8 @@ The `createTemplateAction` takes an object which specifies the following: - `id` - A unique ID for your custom action. We encourage you to namespace these in some way so that they won't collide with future built-in actions that we may ship with the `scaffolder-backend` plugin. -- `description` - An optional field to describe the purpose of the action. This will populate in the `/create/actions` endpoint. +- `description` - An optional field to describe the purpose of the action. This will populate in the `/create/actions` + endpoint. - `schema.input` - A `zod` or JSON schema object for input values to your function - `schema.output` - A `zod` or JSON schema object for values which are output from the function using `ctx.output` @@ -132,18 +141,24 @@ export const createNewFileAction = () => { ### Naming Conventions -Try to keep names consistent for both your own custom actions, and any actions contributed to open source. We've found that a separation of `:` and using a verb as the last part of the name works well. -We follow `provider:entity:verb` or as close to this as possible for our built in actions. For example, `github:actions:create` or `github:repo:create`. +Try to keep names consistent for both your own custom actions, and any actions contributed to open source. We've found +that a separation of `:` and using a verb as the last part of the name works well. +We follow `provider:entity:verb` or as close to this as possible for our built in actions. For example, +`github:actions:create` or `github:repo:create`. Also feel free to use your company name to namespace them if you prefer too, for example `acme:file:create` like above. -Prefer to use `camelCase` over `snake_case` or `kebab-case` for these actions if possible, which leads to better reading and writing of template entity definitions. +Prefer to use `camelCase` over `snake_case` or `kebab-case` for these actions if possible, which leads to better reading +and writing of template entity definitions. -> We're aware that there are some exceptions to this, but try to follow as close as possible. We'll be working on migrating these in the repository over time too. +> We're aware that there are some exceptions to this, but try to follow as close as possible. We'll be working on +> migrating these in the repository over time too. ### Adding a TemplateExample -A TemplateExample is a predefined structure that can be used to create custom actions in your software templates. It serves as a blueprint for users to understand how to use a specific action and its fields as well as to ensure consistency and standardization across different custom actions. +A TemplateExample is a predefined structure that can be used to create custom actions in your software templates. It +serves as a blueprint for users to understand how to use a specific action and its fields as well as to ensure +consistency and standardization across different custom actions. #### Define a TemplateExample and add to your Custom Action @@ -199,7 +214,8 @@ argument. It looks like the following: ## Registering Custom Actions -To register your new custom action in the Backend System you will need to create a backend module. Here is a very simplified example of how to do that: +To register your new custom action in the Backend System you will need to create a backend module. Here is a very +simplified example of how to do that: ```ts title="packages/backend/src/index.ts" /* highlight-add-start */ @@ -233,7 +249,8 @@ backend.add(import('@backstage/plugin-scaffolder-backend')); backend.add(scaffolderModuleCustomExtensions); ``` -If your custom action requires core services such as `config` or `cache` they can be imported in the dependencies and passed to the custom action function. +If your custom action requires core services such as `config` or `cache` they can be imported in the dependencies and +passed to the custom action function. ```ts title="packages/backend/src/index.ts" import { @@ -243,17 +260,17 @@ import { ... - env.registerInit({ - deps: { - scaffolder: scaffolderActionsExtensionPoint, - cache: coreServices.cache, - config: coreServices.rootConfig, - }, - async init({ scaffolder, cache, config }) { - scaffolder.addActions( - customActionNeedingCacheAndConfig({ cache: cache, config: config }), - ); - }) +env.registerInit({ + deps: { + scaffolder: scaffolderActionsExtensionPoint, + cache: coreServices.cache, + config: coreServices.rootConfig, + }, + async init({scaffolder, cache, config}) { + scaffolder.addActions( + customActionNeedingCacheAndConfig({cache: cache, config: config}), + ); + }) ``` ### Using Checkpoints in Custom Actions (Experimental) @@ -281,6 +298,13 @@ You have to define the unique key in scope of the scaffolder task for your check will check if the checkpoint with such key was already executed or not, if yes, and the run was successful, the callback will be skipped and instead the stored value will be returned. +Whenever you change the return type of the checkpoint, we encourage you to change the ID. +For example, you can embed the versioning or another indicator for that (instead of using key `create.projects`, it can +be `create.projects.v1`). +If you'll preserve the same key, and you'll try to restart the affected task, it will fail on this checkpoint. +The cached result will not match with the expected updated return type. +By changing the key, you'll invalidate the cache of the checkpoint. + ### Register Custom Actions with the Legacy Backend System Once you have your Custom Action ready for usage with the scaffolder, you'll diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index 5a9044842f..9444dc9b6e 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -92,10 +92,11 @@ the source code hosting provider. Notice that instead of the `dir:` prefix, the Note, just as it's possible to specify a subdirectory with the `dir:` prefix, you can also provide a path to a non-root directory inside the repository which -contains the `mkdocs.yml` file and `docs/` directory. +contains the `mkdocs.yml` file and `docs/` directory. It is important that it is +suffixed with a '/' in order for relative path resolution to work consistently. e.g. -`url:https://github.com/backstage/backstage/tree/master/plugins/techdocs-backend/examples/documented-component` +`url:https://github.com/backstage/backstage/tree/master/plugins/techdocs-backend/examples/documented-component/` ### Why is URL Reader faster than a git clone? diff --git a/docs/frontend-system/building-apps/08-migrating.md b/docs/frontend-system/building-apps/08-migrating.md index 273c0cffbd..304bc0f84b 100644 --- a/docs/frontend-system/building-apps/08-migrating.md +++ b/docs/frontend-system/building-apps/08-migrating.md @@ -500,7 +500,84 @@ Continue this process for each of your legacy routes until you have migrated all ### Entity Pages -The entity pages are typically defined in `packages/app/src/components/catalog` and rendered as a child of the `/catalog/:namespace/:kind/:name` route. The entity pages are typically quite large and bringing in content from quite a lot of different plugins. At the moment we do not provide a way to gradually migrate entity pages to the new system, although that is planned as a future improvement. This means that the entire entity page and all of its plugins need to be migrated at once, including any other usages of those plugins. +The entity pages are typically defined in `packages/app/src/components/catalog` and rendered as a child of the `/catalog/:namespace/:kind/:name` route. The entity pages are typically quite large and bringing in content from quite a lot of different plugins. To help gradually migrate entity pages we provide the `entityPage` option in the `convertLegacyApp` helper. This option lets you pass in an entity page app element tree that will be converted to extensions that are added to the features returned from `convertLegacyApp`. + +To start the gradual migration of entity pages, add your `entityPages` to the `convertLegacyApp` call: + +```tsx title="in packages/app/src/App.tsx" +/* highlight-remove-next-line */ +const legacyFeatures = convertLegacyApp(routes); +/* highlight-add-next-line */ +const legacyFeatures = convertLegacyApp(routes, { entityPage }); +``` + +Next, you will need to fully migrate the catalog plugin itself. This is because only a single version of a plugin can be installed in the app at a time, so in order to start using the new version of the catalog plugin you need to remove all usage of the old one. This includes both the routes and entity pages. You will need to keep the structural helpers for the entity pages, such as `EntityLayout` and `EntitySwitch`, but remove any extensions like the `` and entity cards and content like `` and ``. + +Remove the following routes: + +```tsx title="in packages/app/src/App.tsx" +const routes = ( + + ... + {/* highlight-remove-start */} + } /> + } + > + {entityPage} + + {/* highlight-remove-end */} + ... + +); +``` + +And explicitly install the catalog plugin before the converted legacy features: + +```tsx title="in packages/app/src/App.tsx" +/* highlight-add-next-line */ +import { default as catalogPlugin } from '@backstage/plugin-catalog/alpha'; + +const app = createApp({ + /* highlight-remove-next-line */ + features: [optionsModule, ...legacyFeatures], + /* highlight-add-next-line */ + features: [catalogPlugin, optionsModule, ...legacyFeatures], +}); +``` + +If you are not using the default `` you can install your custom catalog page as an override for now instead, and fully migrate it to the new system later. + +```tsx title="in packages/app/src/App.tsx" +/* highlight-remove-start */ +const catalogPluginOverride = catalogPlugin.withOverrides({ + extensions: [ + catalogPlugin.getExtension('page:catalog').override({ + params: { + loader: async () => ( + {/* ... */}} + /> + ), + }, + }), + ], +}); +/* highlight-remove-end */ + +const app = createApp({ + /* highlight-remove-next-line */ + features: [catalogPlugin, optionsModule, ...legacyFeatures], + /* highlight-add-next-line */ + features: [catalogPluginOverride, optionsModule, ...legacyFeatures], +}); +``` + +At this point you should be able to run the app and see that you're not using the new version of the catalog plugin. If you navigate to the entity pages you will likely see a lot of duplicate content at the bottom of the page. These are the duplicates of the entity cards provided by the catalog plugin itself that we mentioned earlier that you need to remove. Clean up the entity pages by removing cards and content from the catalog plugin such as `` and ``. + +Once the cleanup is complete you should be left with clean entity pages that are built using a mix of the old and new frontend system. From this point you can continue to gradually migrate plugins that provide content for the entity pages, until all plugins have been fully moved to the new system and the `entityPage` option can be removed. ### Sidebar diff --git a/docs/overview/versioning-policy.md b/docs/overview/versioning-policy.md index e108161281..669a982e1a 100644 --- a/docs/overview/versioning-policy.md +++ b/docs/overview/versioning-policy.md @@ -106,7 +106,7 @@ be part of the `1.12` Backstage release. The following versioning policy applies to all packages: - Breaking changes are noted in the changelog, and documentation is updated. -- Breaking changes are prefixed with `**BREAKING**: ` in the changelog. +- Breaking changes are prefixed with `**BREAKING**:` in the changelog. - All public exports are considered stable and will have an entry in the changelog. - Breaking changes are recommended to document a clear upgrade path in the @@ -195,4 +195,4 @@ The Backstage project recommends and supports using PostgreSQL for persistent st The PostgreSQL [versioning policy](https://www.postgresql.org/support/versioning/) is to release a new major version every year with new features which is then supported for 5 years after its initial release. -Our policy mirrors the PostgreSQL versioning policy - we will support the last 5 major versions. We will also test the newest and oldest versions in that range. For example, if the range we support is currently 12 to 16, then we would only test 12 and 16 explicitly. +Our policy mirrors the PostgreSQL versioning policy - we will support the last 5 major versions. We will also test the newest and oldest versions in that range. For example, if the range we support is currently 13 to 17, then we would only test 13 and 17 explicitly. diff --git a/docs/plugins/proxying.md b/docs/plugins/proxying.md index 0e57a9a72a..26329f8475 100644 --- a/docs/plugins/proxying.md +++ b/docs/plugins/proxying.md @@ -110,13 +110,13 @@ There are also additional settings: from the target. By default, the proxy will only forward safe HTTP request headers to the target. -Those are based on the headers that are considered safe for CORS and includes +These are based on the headers that are considered safe for CORS and include headers like `content-type` or `last-modified`, as well as all headers that are -set by the proxy. If the proxy should forward other headers like +set by the proxy. If the proxy should forward other headers, like `authorization`, this must be enabled by the `allowedHeaders` config, for example `allowedHeaders: ['Authorization']`. This should help to not accidentally forward confidential headers (`cookie`, `X-Auth-Request-User`) to -third-parties. +third parties. The same logic applies to headers that are sent from the target back to the frontend. diff --git a/docs/releases/v1.37.0-next.2-changelog.md b/docs/releases/v1.37.0-next.2-changelog.md new file mode 100644 index 0000000000..cde54079f5 --- /dev/null +++ b/docs/releases/v1.37.0-next.2-changelog.md @@ -0,0 +1,1927 @@ +# Release v1.37.0-next.2 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.37.0-next.2](https://backstage.github.io/upgrade-helper/?to=1.37.0-next.2) + +## @backstage/app-defaults@1.6.0-next.1 + +### Minor Changes + +- 12f8e01: **BREAKING**: The default `DiscoveryApi` implementation has been switched to use `FrontendHostDiscovery`, which adds support for the `discovery.endpoints` configuration. + + This is marked as a breaking change because it will cause any existing `discovery.endpoints` configuration to be picked up and used, which may break existing setups. + + For example, consider the following configuration: + + ```yaml + app: + baseUrl: https://backstage.acme.org + + backend: + baseUrl: https://backstage.internal.acme.org + + discovery: + endpoints: + - target: https://catalog.internal.acme.org/api/{{pluginId}} + plugins: [catalog] + ``` + + This will now cause requests from the frontend towards the `catalog` plugin to be routed to `https://catalog.internal.acme.org/api/catalog`, but this might not be reachable from the frontend. To fix this, you should update the `discovery.endpoints` configuration to only override the internal URL of the plugin: + + ```yaml + discovery: + endpoints: + - target: + internal: https://catalog.internal.acme.org/api/{{pluginId}} + plugins: [catalog] + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/theme@0.6.4 + - @backstage/plugin-permission-react@0.4.31 + +## @backstage/cli@0.31.0-next.1 + +### Minor Changes + +- 5b70679: **BREAKING**: ESLint warnings no longer trigger system exit codes like errors do. + + Set the max number of warnings to `-1` during linting to enable the gradual adoption of new ESLint rules. To restore the previous behavior, include the `--max-warnings 0` flag in the `backstage-cli lint` command. + +### Patch Changes + +- e0b226b: build(deps): bump `esbuild` from 0.24.2 to 0.25.0 +- 4d45498: Fixed the package prepack command so that it no longer produces unnecessary `index` entries in the `typesVersions` map, which could cause `/index` to be added when automatically adding imports. +- f8bd342: Fix a bug in the translation of the deprecated `--scope` option for the `new` command that could cause plugins to have `backstage-backstage-plugin` in their name. +- Updated dependencies + - @backstage/config-loader@1.10.0-next.0 + - @backstage/integration@1.16.2-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/eslint-plugin@0.1.10 + - @backstage/release-manifests@0.0.12 + - @backstage/types@1.2.1 + +## @backstage/config-loader@1.10.0-next.0 + +### Minor Changes + +- 2fd73aa: The include transforms applied during config loading will now only apply to the known keys `$file`, `$env`, and `$include`. Any other key that begins with a \`# @backstage/config-loader will now be passed through as is. + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## @backstage/core-app-api@1.16.0-next.0 + +### Minor Changes + +- 9262001: The default auth injection middleware for the `FetchApi` will now also take configuration under `discovery.endpoints` into consideration when deciding whether to include credentials or not. +- 12f8e01: The `discovery.endpoints` configuration no longer requires both `internal` and `external` target when using the object form, instead falling back to the default. + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + +## @backstage/core-compat-api@0.4.0-next.2 + +### Minor Changes + +- 8250ffe: **BREAKING**: Dropped support for the removed opaque `@backstage/ExtensionOverrides` and `@backstage/BackstagePlugin` types. + +### Patch Changes + +- e7fab55: Added the `entityPage` option to `convertLegacyApp`, which you can read more about in the [app migration docs](https://backstage.io/docs/frontend-system/building-apps/migrating#entity-pages). +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/version-bridge@1.0.11 + +## @backstage/create-app@0.6.0-next.2 + +### Minor Changes + +- 31731b0: Upgraded the TypeScript version in the template to `5.8`. + +### Patch Changes + +- 19e5c3f: Added link to multi-stage Dockerfile documentation as alternative option +- Updated dependencies + - @backstage/cli-common@0.1.15 + +## @backstage/frontend-app-api@0.11.0-next.2 + +### Minor Changes + +- abcdf44: **BREAKING**: The returned object from `createSpecializedApp` no longer contains a `createRoot()` method, and it instead now contains `apis` and `tree`. + + You can replace existing usage of `app.createRoot()` with the following: + + ```ts + const root = tree.root.instance?.getData(coreExtensionData.reactElement); + ``` + +- 8250ffe: **BREAKING**: Dropped support for the removed opaque `@backstage/ExtensionOverrides` and `@backstage/BackstagePlugin` types. + +### Patch Changes + +- 4d18b55: It's now possible to provide a middleware that wraps all extension factories by passing an `extensionFactoryMiddleware` to either `createApp()` or `createSpecializedApp()`. +- Updated dependencies + - @backstage/frontend-defaults@0.2.0-next.2 + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + +## @backstage/frontend-defaults@0.2.0-next.2 + +### Minor Changes + +- 8250ffe: **BREAKING**: Dropped support for the removed opaque `@backstage/ExtensionOverrides` and `@backstage/BackstagePlugin` types. + +### Patch Changes + +- 4d18b55: It's now possible to provide a middleware that wraps all extension factories by passing an `extensionFactoryMiddleware` to either `createApp()` or `createSpecializedApp()`. +- abcdf44: Internal refactor to match updated `createSpecializedApp`. +- e3f19db: Feature discovery and resolution logic used in `createApp` is now exposed via the `discoverAvailableFeatures` and `resolveAsyncFeatures` functions respectively. +- Updated dependencies + - @backstage/frontend-app-api@0.11.0-next.2 + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-app@0.1.7-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/frontend-plugin-api@0.10.0-next.2 + +### Minor Changes + +- 8250ffe: **BREAKING**: Removed the deprecated `ExtensionOverrides` and `FrontendFeature` types. +- 0d1a397: **BREAKING**: Removed deprecated variant of `createExtensionDataRef` where the ID is passed directly. + +### Patch Changes + +- 5aa7f2c: Added a new Utility API, `DialogApi`, which can be used to show dialogs in the React tree that can collect input from the user. +- e23f5e0: Added new `ExtensionMiddlewareFactory` type. +- a6cb67d: The extensions map for plugins created with `createFrontendPlugin` is now sorted alphabetically by ID in the TypeScript type. +- Updated dependencies + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + +## @backstage/frontend-test-utils@0.3.0-next.2 + +### Minor Changes + +- bba525b: **BREAKING**: Removed deprecated `setupRequestMockHandlers` which was replaced by `registerMswTestHooks`. + +### Patch Changes + +- f861bfc: Added a `initialRouteEntries` option to `renderInTestApp`. +- f861bfc: The `renderInTestApp` helper now provides a default mock config with mock values for both `app.baseUrl` and `backend.baseUrl`. +- abcdf44: Internal refactor to match updated `createSpecializedApp`. +- Updated dependencies + - @backstage/frontend-app-api@0.11.0-next.2 + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-app@0.1.7-next.2 + - @backstage/test-utils@1.7.6-next.0 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + +## @backstage/plugin-catalog@1.28.0-next.2 + +### Minor Changes + +- 247a40b: Now a custom entity page header can be passed as input to the default entity page. +- 93533bd: The order in which group tabs appear on the entity page has been changed. + + ### Before + + Previously, entity contents determined the order in which groups were rendered, so a group was rendered as soon as its first entity content was detected. + + ### After + + Groups are now rendered first by default based on their order in the `app-config.yaml` file: + + ```diff + app: + extensions: + - page:catalog/entity: + + config: + + groups: + + # this will be the first tab of the default entity page + + - deployment: + + title: Deployment + + # this will be the second tab of the default entiy page + + - documentation: + + title: Documentation + ``` + + If you wish to place a normal tab before a group, you must add the tab to a group and place the group in the order you wish it to appear on the entity page (groups that contains only one tab are rendered as normal tabs). + + ```diff + app: + extensions: + - page:catalog/entity: + config: + groups: + + # Example placing the overview tab first + + - overview: + + title: Overview + - deployment: + title: Deployment + # this will be the second tab of the default entiy page + - documentation: + title: Documentation + - entity-content:catalog/overview: + + config: + + group: 'overview' + ``` + +### Patch Changes + +- 31731b0: Internal refactor to avoid `expiry-map` dependency. +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/plugin-search-react@1.8.7-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-react@0.4.31 + - @backstage/plugin-scaffolder-common@1.5.10-next.0 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-catalog-react@1.16.0-next.2 + +### Minor Changes + +- 7f57365: Add support for a new entity predicate syntax when defining `filter`s related to the blueprints exported via `/alpha` for the new frontend system. For more information, see the [entity filters documentation](https://backstage.io/docs/features/software-catalog/catalog-customization#advanced-customization#entity-filters). +- 247a40b: Introduces a new `EntityHeaderBlueprint` that allows you to override the default entity page header. + + ```jsx + import { EntityHeaderBlueprint } from '@backstage/plugin-catalog-react/alpha'; + + EntityHeaderBlueprint.make({ + name: 'my-default-header', + params: { + loader: () => + import('./MyDefaultHeader').then(m => ), + }, + }); + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/frontend-test-utils@0.3.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-react@0.4.31 + +## @backstage/plugin-scaffolder-backend@1.31.0-next.2 + +### Minor Changes + +- 36677bb: Support new `createTemplateAction` type, and convert `catalog:fetch` action to new way of defining actions. +- 2b1e50d: use CreatedTemplate[Filter|Global*] as canonical template extensions in scaffolder plugin + +### Patch Changes + +- e0b226b: build(deps): bump `esbuild` from 0.24.2 to 0.25.0 +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- 59dcf37: Fixed bug in fs:delete causing no files to be deleted on windows machines +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-gitlab@0.8.1-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.7-next.2 + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/plugin-scaffolder-backend-module-github@0.6.1-next.2 + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.7-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.8-next.2 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.7-next.2 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.7-next.2 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.7-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.28-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.6-next.1 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-node@0.8.9-next.1 + - @backstage/plugin-scaffolder-common@1.5.10-next.0 + +## @backstage/plugin-scaffolder-node@0.8.0-next.2 + +### Minor Changes + +- 1a58846: **DEPRECATION**: We've deprecated the old way of defining actions using `createTemplateAction` with raw `JSONSchema` and type parameters, as well as using `zod` through an import. You can now use the new format to define `createTemplateActions` with `zod` provided by the framework. This change also removes support for `logStream` in the `context` as well as moving the `logger` to an instance of `LoggerService`. + + Before: + + ```ts + createTemplateAction<{ repoUrl: string }, { test: string }>({ + id: 'test', + schema: { + input: { + type: 'object', + required: ['repoUrl'], + properties: { + repoUrl: { type: 'string' }, + }, + }, + output: { + type: 'object', + required: ['test'], + properties: { + test: { type: 'string' }, + }, + }, + }, + handler: async ctx => { + ctx.logStream.write('blob'); + }, + }); + + // or + + createTemplateAction({ + id: 'test', + schema: { + input: z.object({ + repoUrl: z.string(), + }), + output: z.object({ + test: z.string(), + }), + }, + handler: async ctx => { + ctx.logStream.write('something'); + }, + }); + ``` + + After: + + ```ts + createTemplateAction({ + id: 'test', + schema: { + input: { + repoUrl: d => d.string(), + }, + output: { + test: d => d.string(), + }, + }, + handler: async ctx => { + // you can just use ctx.logger.log('...'), or if you really need a log stream you can do this: + const logStream = new PassThrough(); + logStream.on('data', chunk => { + ctx.logger.info(chunk.toString()); + }); + }, + }); + ``` + +### Patch Changes + +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-scaffolder-common@1.5.10-next.0 + +## @backstage/plugin-scaffolder-node-test-utils@0.2.0-next.2 + +### Minor Changes + +- 36677bb: Use update `createTemplateAction` kinds + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/backend-test-utils@1.3.1-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/types@1.2.1 + +## @backstage/backend-app-api@1.2.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.0-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-permission-node@0.8.9-next.1 + +## @backstage/backend-defaults@0.8.2-next.2 + +### Patch Changes + +- 12f8e01: The `discovery.endpoints` configuration no longer requires both `internal` and `external` target when using the object form, instead falling back to the default. +- Updated dependencies + - @backstage/config-loader@1.10.0-next.0 + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-app-api@1.2.1-next.2 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-permission-node@0.8.9-next.1 + +## @backstage/backend-dynamic-feature-service@0.6.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/plugin-catalog-backend@1.32.0-next.2 + - @backstage/config-loader@1.10.0-next.0 + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/plugin-events-backend@0.4.4-next.2 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-app-node@0.1.31-next.2 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-node@0.8.9-next.1 + - @backstage/plugin-search-backend-node@1.3.9-next.1 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/backend-test-utils@1.3.1-next.2 + +### Patch Changes + +- 37c6510: Moved `@types/jest` to `devDependencies`. +- Updated dependencies + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-app-api@1.2.1-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + +## @backstage/core-components@0.16.5-next.1 + +### Patch Changes + +- 48aab13: Add i18n support for scaffolder-react plugin +- Updated dependencies + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.4 + - @backstage/version-bridge@1.0.11 + +## @backstage/dev-utils@1.1.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/app-defaults@1.6.0-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/theme@0.6.4 + +## @backstage/integration@1.16.2-next.0 + +### Patch Changes + +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/integration-react@1.2.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + +## @backstage/repo-tools@0.13.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.0-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/errors@1.2.7 + +## @techdocs/cli@1.9.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.13.1-next.2 + +## @backstage/test-utils@1.7.6-next.0 + +### Patch Changes + +- 37c6510: Moved `@types/jest` to `devDependencies`. +- Updated dependencies + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-react@0.4.31 + +## @backstage/plugin-api-docs@0.12.5-next.2 + +### Patch Changes + +- 74871cc: Use consistent Typography in Entity HasApisCard +- Updated dependencies + - @backstage/plugin-catalog@1.28.0-next.2 + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-react@0.4.31 + +## @backstage/plugin-app@0.1.7-next.2 + +### Patch Changes + +- 0aa9d82: Added implementation of the new `DialogApi`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-react@0.4.31 + +## @backstage/plugin-app-backend@0.5.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.0-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-app-node@0.1.31-next.2 + - @backstage/plugin-auth-node@0.6.1-next.1 + +## @backstage/plugin-app-node@0.1.31-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.0-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + +## @backstage/plugin-app-visualizer@0.1.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + +## @backstage/plugin-auth-backend@0.24.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend-module-oauth2-provider@0.4.1-next.2 + - @backstage/plugin-auth-backend-module-oidc-provider@0.4.1-next.2 + - @backstage/plugin-auth-backend-module-okta-provider@0.2.1-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.4.1-next.1 + - @backstage/plugin-auth-backend-module-auth0-provider@0.2.1-next.1 + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.4.1-next.2 + - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.6-next.1 + - @backstage/plugin-auth-backend-module-bitbucket-provider@0.3.1-next.1 + - @backstage/plugin-auth-backend-module-bitbucket-server-provider@0.2.1-next.1 + - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.4.1-next.1 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.4.1-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.3.1-next.1 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.3.1-next.1 + - @backstage/plugin-auth-backend-module-google-provider@0.3.1-next.1 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.3.1-next.1 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.6-next.1 + - @backstage/plugin-auth-backend-module-onelogin-provider@0.3.1-next.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-catalog-node@1.16.1-next.1 + +## @backstage/plugin-auth-backend-module-aws-alb-provider@0.4.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-backend@0.24.4-next.2 + - @backstage/plugin-auth-node@0.6.1-next.1 + +## @backstage/plugin-auth-backend-module-oauth2-provider@0.4.1-next.2 + +### Patch Changes + +- ce15e30: Fixed repository url in `README.md` +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + +## @backstage/plugin-auth-backend-module-oidc-provider@0.4.1-next.2 + +### Patch Changes + +- ce15e30: Fixed repository url in `README.md` +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/plugin-auth-backend@0.24.4-next.2 + - @backstage/plugin-auth-node@0.6.1-next.1 + +## @backstage/plugin-auth-backend-module-okta-provider@0.2.1-next.2 + +### Patch Changes + +- ce15e30: Fixed repository url in `README.md` +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + +## @backstage/plugin-auth-react@0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + +## @backstage/plugin-bitbucket-cloud-common@0.2.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + +## @backstage/plugin-catalog-backend@1.32.0-next.2 + +### Patch Changes + +- 4306303: Added a fix in `@backstage/plugin-catalog-backend` to prevent duplicate path keys in entity search if only casing is different. +- 5243aa4: Fixed an issue occurred when authorizing permissions using custom rules passed via the `PermissionsRegistryService`. +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-openapi-utils@0.5.1-next.1 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-node@0.8.9-next.1 + - @backstage/plugin-search-backend-module-catalog@0.3.2-next.1 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-catalog-backend-module-aws@0.4.9-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.15 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-kubernetes-common@0.9.4-next.0 + +## @backstage/plugin-catalog-backend-module-azure@0.3.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.4.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-bitbucket-cloud-common@0.2.28-next.0 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.3.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-node@1.16.1-next.1 + +## @backstage/plugin-catalog-backend-module-gerrit@0.2.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-node@1.16.1-next.1 + +## @backstage/plugin-catalog-backend-module-github@0.7.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.32.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + +## @backstage/plugin-catalog-backend-module-github-org@0.3.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-backend-module-github@0.7.11-next.2 + - @backstage/plugin-catalog-node@1.16.1-next.1 + +## @backstage/plugin-catalog-backend-module-gitlab@0.6.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.2.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/plugin-catalog-backend-module-gitlab@0.6.4-next.2 + - @backstage/plugin-catalog-node@1.16.1-next.1 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.6.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.32.0-next.2 + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-permission-common@0.8.4 + +## @backstage/plugin-catalog-backend-module-ldap@0.11.3-next.2 + +### Patch Changes + +- e43f41b: Fix `config.d.ts` for `ldapOrg` being incorrect. The documentation says a single + object or an array are accepted, but the definition only allows an object. +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + +## @backstage/plugin-catalog-backend-module-logs@0.1.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.32.0-next.2 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + +## @backstage/plugin-catalog-backend-module-openapi@0.2.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + +## @backstage/plugin-catalog-graph@0.4.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/types@1.2.1 + +## @backstage/plugin-catalog-import@0.12.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/integration@1.16.2-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/plugin-catalog-common@1.1.3 + +## @backstage/plugin-catalog-unprocessed-entities@0.2.15-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + +## @backstage/plugin-config-schema@0.1.66-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## @backstage/plugin-devtools@0.1.25-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/plugin-devtools-common@0.1.15 + - @backstage/plugin-permission-react@0.4.31 + +## @backstage/plugin-devtools-backend@0.5.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.0-next.0 + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-devtools-common@0.1.15 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-node@0.8.9-next.1 + +## @backstage/plugin-events-backend@0.4.4-next.2 + +### Patch Changes + +- b95aa77: add `addHttpPostBodyParser` to events extension to allow body parse customization. This feature will enhance flexibility in handling HTTP POST requests in event-related operations. +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-openapi-utils@0.5.1-next.1 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## @backstage/plugin-events-backend-module-aws-sqs@0.4.9-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + +## @backstage/plugin-events-backend-module-azure@0.2.18-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.2.18-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + +## @backstage/plugin-events-backend-module-gerrit@0.2.18-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + +## @backstage/plugin-events-backend-module-github@0.2.18-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + +## @backstage/plugin-events-backend-module-gitlab@0.2.18-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + +## @backstage/plugin-events-backend-test-utils@0.1.42-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + +## @backstage/plugin-events-node@0.4.9-next.2 + +### Patch Changes + +- b95aa77: add `addHttpPostBodyParser` to events extension to allow body parse customization. This feature will enhance flexibility in handling HTTP POST requests in event-related operations. +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## @backstage/plugin-home@0.8.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/theme@0.6.4 + - @backstage/plugin-home-react@0.1.24-next.1 + +## @backstage/plugin-home-react@0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + +## @backstage/plugin-kubernetes@0.12.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/plugin-kubernetes-common@0.9.4-next.0 + - @backstage/plugin-kubernetes-react@0.5.5-next.2 + - @backstage/plugin-permission-react@0.4.31 + +## @backstage/plugin-kubernetes-cluster@0.0.23-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/plugin-kubernetes-common@0.9.4-next.0 + - @backstage/plugin-kubernetes-react@0.5.5-next.2 + - @backstage/plugin-permission-react@0.4.31 + +## @backstage/plugin-kubernetes-react@0.5.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-kubernetes-common@0.9.4-next.0 + +## @backstage/plugin-notifications@0.5.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-signals-react@0.0.10 + +## @backstage/plugin-notifications-backend@0.5.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.13-next.2 + - @backstage/plugin-signals-node@0.1.18-next.2 + +## @backstage/plugin-notifications-backend-module-email@0.3.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.13-next.2 + +## @backstage/plugin-notifications-node@0.2.13-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-signals-node@0.1.18-next.2 + +## @backstage/plugin-org@0.6.37-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/plugin-catalog-common@1.1.3 + +## @backstage/plugin-org-react@0.1.36-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + +## @backstage/plugin-scaffolder@1.29.0-next.2 + +### Patch Changes + +- 3db64ba: Disable the submit button on creating +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/plugin-scaffolder-react@1.14.6-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-react@0.4.31 + - @backstage/plugin-scaffolder-common@1.5.10-next.0 + +## @backstage/plugin-scaffolder-backend-module-azure@0.2.7-next.2 + +### Patch Changes + +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.8-next.2 + +### Patch Changes + +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.7-next.2 + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.7-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.7-next.2 + +### Patch Changes + +- c56a279: Added `bitbucketCloud:branchRestriction:create` to allow users to create bitbucket cloud branch restrictions in templates +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- e279c30: Fixing spelling mistake in `jsonschema` +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-bitbucket-cloud-common@0.2.28-next.0 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.7-next.2 + +### Patch Changes + +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.3.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## @backstage/plugin-scaffolder-backend-module-gcp@0.2.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-gerrit@0.2.7-next.2 + +### Patch Changes + +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-gitea@0.2.7-next.2 + +### Patch Changes + +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-github@0.6.1-next.2 + +### Patch Changes + +- 9391f58: Pass `undefined` to some parameters for `createOrUpdateEnvironment` as these values are not always supported in different plans of GitHub +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.8.1-next.2 + +### Patch Changes + +- 0df33ea: fix: Creating a repository in a user namespace would always lead to an error +- ac58f84: Made gitlab:issue:edit action idempotent. +- a75e18f: Change the if statement in the catch block to match what the new version of Gitbeaker will return +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-notifications@0.1.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.13-next.2 + +## @backstage/plugin-scaffolder-backend-module-rails@0.5.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.2.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.4.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/plugin-scaffolder-node-test-utils@0.2.0-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/types@1.2.1 + +## @backstage/plugin-scaffolder-react@1.14.6-next.2 + +### Patch Changes + +- 48aab13: Add i18n support for scaffolder-react plugin +- 3db64ba: Disable the submit button on creating +- 34ea3f5: Updated dependency `flatted` to `3.3.3`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-permission-react@0.4.31 + - @backstage/plugin-scaffolder-common@1.5.10-next.0 + +## @backstage/plugin-search@1.4.24-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/plugin-search-react@1.8.7-next.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-search-backend@1.8.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/backend-openapi-utils@0.5.1-next.1 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-node@0.8.9-next.1 + - @backstage/plugin-search-backend-node@1.3.9-next.1 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-search-backend-module-techdocs@0.3.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-backend-node@1.3.9-next.1 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-node@1.13.1-next.2 + +## @backstage/plugin-search-react@1.8.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-signals@0.0.17-next.2 + +### Patch Changes + +- ac3e8c0: Fixed multiple signal connection attempts when there are multiple subscriptions at the same time +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-react@0.0.10 + +## @backstage/plugin-signals-backend@0.3.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-signals-node@0.1.18-next.2 + +## @backstage/plugin-signals-node@0.1.18-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + +## @backstage/plugin-techdocs@1.12.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-search-react@1.8.7-next.2 + - @backstage/plugin-techdocs-react@1.2.15-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/theme@0.6.4 + - @backstage/plugin-auth-react@0.1.13-next.1 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-common@0.1.0 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.46-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.28.0-next.2 + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/test-utils@1.7.6-next.0 + - @backstage/plugin-techdocs@1.12.4-next.2 + - @backstage/plugin-search-react@1.8.7-next.2 + - @backstage/plugin-techdocs-react@1.2.15-next.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/integration-react@1.2.5-next.0 + +## @backstage/plugin-techdocs-backend@1.11.7-next.2 + +### Patch Changes + +- 7828186: Minor type fix +- 8f03776: Properly clean up temporary files on build failures +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-backend-module-techdocs@0.3.7-next.2 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-node@1.13.1-next.2 + +## @backstage/plugin-techdocs-module-addons-contrib@1.1.22-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-techdocs-react@1.2.15-next.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/integration-react@1.2.5-next.0 + +## @backstage/plugin-techdocs-node@1.13.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.15 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-common@0.1.0 + +## @backstage/plugin-techdocs-react@1.2.15-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/version-bridge@1.0.11 + +## @backstage/plugin-user-settings@0.8.20-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-react@0.0.10 + - @backstage/plugin-user-settings-common@0.0.1 + +## @backstage/plugin-user-settings-backend@0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-signals-node@0.1.18-next.2 + - @backstage/plugin-user-settings-common@0.0.1 + +## example-app@0.2.107-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.28.0-next.2 + - @backstage/frontend-app-api@0.11.0-next.2 + - @backstage/cli@0.31.0-next.1 + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/plugin-signals@0.0.17-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/plugin-scaffolder-react@1.14.6-next.2 + - @backstage/app-defaults@1.6.0-next.1 + - @backstage/plugin-scaffolder@1.29.0-next.2 + - @backstage/plugin-api-docs@0.12.5-next.2 + - @backstage/plugin-catalog-graph@0.4.17-next.2 + - @backstage/plugin-catalog-import@0.12.11-next.2 + - @backstage/plugin-org@0.6.37-next.2 + - @backstage/plugin-techdocs@1.12.4-next.2 + - @backstage/plugin-user-settings@0.8.20-next.2 + - @backstage/plugin-search-react@1.8.7-next.2 + - @backstage/plugin-catalog-unprocessed-entities@0.2.15-next.2 + - @backstage/plugin-devtools@0.1.25-next.2 + - @backstage/plugin-home@0.8.6-next.2 + - @backstage/plugin-kubernetes@0.12.5-next.2 + - @backstage/plugin-notifications@0.5.3-next.2 + - @backstage/plugin-search@1.4.24-next.2 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.22-next.2 + - @backstage/plugin-techdocs-react@1.2.15-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/theme@0.6.4 + - @backstage/plugin-auth-react@0.1.13-next.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-kubernetes-cluster@0.0.23-next.2 + - @backstage/plugin-permission-react@0.4.31 + - @backstage/plugin-search-common@1.2.17 + +## example-app-next@0.0.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.28.0-next.2 + - @backstage/frontend-app-api@0.11.0-next.2 + - @backstage/frontend-defaults@0.2.0-next.2 + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/cli@0.31.0-next.1 + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/plugin-signals@0.0.17-next.2 + - @backstage/plugin-app@0.1.7-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/plugin-scaffolder-react@1.14.6-next.2 + - @backstage/app-defaults@1.6.0-next.1 + - @backstage/plugin-scaffolder@1.29.0-next.2 + - @backstage/plugin-api-docs@0.12.5-next.2 + - @backstage/plugin-catalog-graph@0.4.17-next.2 + - @backstage/plugin-catalog-import@0.12.11-next.2 + - @backstage/plugin-org@0.6.37-next.2 + - @backstage/plugin-techdocs@1.12.4-next.2 + - @backstage/plugin-user-settings@0.8.20-next.2 + - @backstage/plugin-search-react@1.8.7-next.2 + - @backstage/plugin-app-visualizer@0.1.17-next.2 + - @backstage/plugin-catalog-unprocessed-entities@0.2.15-next.2 + - @backstage/plugin-home@0.8.6-next.2 + - @backstage/plugin-kubernetes@0.12.5-next.2 + - @backstage/plugin-notifications@0.5.3-next.2 + - @backstage/plugin-search@1.4.24-next.2 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.22-next.2 + - @backstage/plugin-techdocs-react@1.2.15-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/theme@0.6.4 + - @backstage/plugin-auth-react@0.1.13-next.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-kubernetes-cluster@0.0.23-next.2 + - @backstage/plugin-permission-react@0.4.31 + - @backstage/plugin-search-common@1.2.17 + +## app-next-example-plugin@0.0.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-components@0.16.5-next.1 + +## example-backend@0.0.36-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.31.0-next.2 + - @backstage/plugin-catalog-backend@1.32.0-next.2 + - @backstage/plugin-techdocs-backend@1.11.7-next.2 + - @backstage/plugin-scaffolder-backend-module-github@0.6.1-next.2 + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/plugin-events-backend@0.4.4-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/plugin-app-backend@0.5.0-next.2 + - @backstage/plugin-auth-backend@0.24.4-next.2 + - @backstage/plugin-auth-backend-module-github-provider@0.3.1-next.1 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.6-next.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.0-next.1 + - @backstage/plugin-catalog-backend-module-openapi@0.2.8-next.2 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.6-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.6-next.1 + - @backstage/plugin-devtools-backend@0.5.3-next.2 + - @backstage/plugin-kubernetes-backend@0.19.4-next.1 + - @backstage/plugin-notifications-backend@0.5.4-next.2 + - @backstage/plugin-permission-backend@0.5.55-next.1 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.6-next.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-node@0.8.9-next.1 + - @backstage/plugin-proxy-backend@0.6.0-next.1 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.8-next.2 + - @backstage/plugin-search-backend@1.8.3-next.2 + - @backstage/plugin-search-backend-module-catalog@0.3.2-next.1 + - @backstage/plugin-search-backend-module-explore@0.2.9-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.3.7-next.2 + - @backstage/plugin-search-backend-node@1.3.9-next.1 + - @backstage/plugin-signals-backend@0.3.2-next.2 + +## example-backend-legacy@0.2.108-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-gitlab@0.8.1-next.2 + - @backstage/plugin-scaffolder-backend@1.31.0-next.2 + - @backstage/plugin-catalog-backend@1.32.0-next.2 + - @backstage/plugin-techdocs-backend@1.11.7-next.2 + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-events-backend@0.4.4-next.2 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-auth-backend@0.24.4-next.2 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.6-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.6-next.1 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-kubernetes-backend@0.19.4-next.1 + - @backstage/plugin-permission-backend@0.5.55-next.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-node@0.8.9-next.1 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.7-next.2 + - @backstage/plugin-scaffolder-backend-module-rails@0.5.7-next.2 + - @backstage/plugin-search-backend@1.8.3-next.2 + - @backstage/plugin-search-backend-module-catalog@0.3.2-next.1 + - @backstage/plugin-search-backend-module-elasticsearch@1.6.6-next.1 + - @backstage/plugin-search-backend-module-explore@0.2.9-next.1 + - @backstage/plugin-search-backend-module-pg@0.5.42-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.3.7-next.2 + - @backstage/plugin-search-backend-node@1.3.9-next.1 + - @backstage/plugin-signals-backend@0.3.2-next.2 + - @backstage/plugin-signals-node@0.1.18-next.2 + +## e2e-test@0.2.26-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.6.0-next.2 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + +## @internal/frontend@0.0.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + +## @internal/scaffolder@0.0.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-scaffolder-react@1.14.6-next.2 + +## techdocs-cli-embedded-app@0.2.106-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.28.0-next.2 + - @backstage/cli@0.31.0-next.1 + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/core-components@0.16.5-next.1 + - @backstage/app-defaults@1.6.0-next.1 + - @backstage/test-utils@1.7.6-next.0 + - @backstage/plugin-techdocs@1.12.4-next.2 + - @backstage/plugin-techdocs-react@1.2.15-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/theme@0.6.4 + +## @internal/plugin-todo-list@1.0.37-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 diff --git a/microsite/data/plugins/facets-cloud.yaml b/microsite/data/plugins/facets-cloud.yaml new file mode 100644 index 0000000000..0f239a77ee --- /dev/null +++ b/microsite/data/plugins/facets-cloud.yaml @@ -0,0 +1,12 @@ +--- +title: Facets.cloud Platform +author: Facets.cloud +authorUrl: 'https://facets.cloud' +category: Deployment +description: | + Show environments, releases and terraform outputs deployed by Facets.cloud Platform. + Plugin includes an Environment Overview, Release Details and Terraform Output ComponentCard. +documentation: https://github.com/Facets-cloud/facets-backstage-plugin +iconUrl: /img/facets-cloud-logo.png +npmPackageName: '@facets-cloud/backstage-plugin' +addedDate: '2025-02-25' diff --git a/microsite/static/img/facets-cloud-logo.png b/microsite/static/img/facets-cloud-logo.png new file mode 100644 index 0000000000..fd9c992f52 Binary files /dev/null and b/microsite/static/img/facets-cloud-logo.png differ diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 8dcb17942e..5950605385 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -2961,90 +2961,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.11.8": - version: 1.11.8 - resolution: "@swc/core-darwin-arm64@npm:1.11.8" +"@swc/core-darwin-arm64@npm:1.11.9": + version: 1.11.9 + resolution: "@swc/core-darwin-arm64@npm:1.11.9" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.11.8": - version: 1.11.8 - resolution: "@swc/core-darwin-x64@npm:1.11.8" +"@swc/core-darwin-x64@npm:1.11.9": + version: 1.11.9 + resolution: "@swc/core-darwin-x64@npm:1.11.9" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.11.8": - version: 1.11.8 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.11.8" +"@swc/core-linux-arm-gnueabihf@npm:1.11.9": + version: 1.11.9 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.11.9" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.11.8": - version: 1.11.8 - resolution: "@swc/core-linux-arm64-gnu@npm:1.11.8" +"@swc/core-linux-arm64-gnu@npm:1.11.9": + version: 1.11.9 + resolution: "@swc/core-linux-arm64-gnu@npm:1.11.9" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.11.8": - version: 1.11.8 - resolution: "@swc/core-linux-arm64-musl@npm:1.11.8" +"@swc/core-linux-arm64-musl@npm:1.11.9": + version: 1.11.9 + resolution: "@swc/core-linux-arm64-musl@npm:1.11.9" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.11.8": - version: 1.11.8 - resolution: "@swc/core-linux-x64-gnu@npm:1.11.8" +"@swc/core-linux-x64-gnu@npm:1.11.9": + version: 1.11.9 + resolution: "@swc/core-linux-x64-gnu@npm:1.11.9" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.11.8": - version: 1.11.8 - resolution: "@swc/core-linux-x64-musl@npm:1.11.8" +"@swc/core-linux-x64-musl@npm:1.11.9": + version: 1.11.9 + resolution: "@swc/core-linux-x64-musl@npm:1.11.9" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.11.8": - version: 1.11.8 - resolution: "@swc/core-win32-arm64-msvc@npm:1.11.8" +"@swc/core-win32-arm64-msvc@npm:1.11.9": + version: 1.11.9 + resolution: "@swc/core-win32-arm64-msvc@npm:1.11.9" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.11.8": - version: 1.11.8 - resolution: "@swc/core-win32-ia32-msvc@npm:1.11.8" +"@swc/core-win32-ia32-msvc@npm:1.11.9": + version: 1.11.9 + resolution: "@swc/core-win32-ia32-msvc@npm:1.11.9" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.11.8": - version: 1.11.8 - resolution: "@swc/core-win32-x64-msvc@npm:1.11.8" +"@swc/core-win32-x64-msvc@npm:1.11.9": + version: 1.11.9 + resolution: "@swc/core-win32-x64-msvc@npm:1.11.9" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.46": - version: 1.11.8 - resolution: "@swc/core@npm:1.11.8" + version: 1.11.9 + resolution: "@swc/core@npm:1.11.9" dependencies: - "@swc/core-darwin-arm64": 1.11.8 - "@swc/core-darwin-x64": 1.11.8 - "@swc/core-linux-arm-gnueabihf": 1.11.8 - "@swc/core-linux-arm64-gnu": 1.11.8 - "@swc/core-linux-arm64-musl": 1.11.8 - "@swc/core-linux-x64-gnu": 1.11.8 - "@swc/core-linux-x64-musl": 1.11.8 - "@swc/core-win32-arm64-msvc": 1.11.8 - "@swc/core-win32-ia32-msvc": 1.11.8 - "@swc/core-win32-x64-msvc": 1.11.8 + "@swc/core-darwin-arm64": 1.11.9 + "@swc/core-darwin-x64": 1.11.9 + "@swc/core-linux-arm-gnueabihf": 1.11.9 + "@swc/core-linux-arm64-gnu": 1.11.9 + "@swc/core-linux-arm64-musl": 1.11.9 + "@swc/core-linux-x64-gnu": 1.11.9 + "@swc/core-linux-x64-musl": 1.11.9 + "@swc/core-win32-arm64-msvc": 1.11.9 + "@swc/core-win32-ia32-msvc": 1.11.9 + "@swc/core-win32-x64-msvc": 1.11.9 "@swc/counter": ^0.1.3 "@swc/types": ^0.1.19 peerDependencies: @@ -3073,7 +3073,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 53aac457ec10ad931e4a15d14be87d4cb53432d2fc2433244835ceb1396e5560f52155e24eadc67bb964bd675b74eea0d99a7570062cd915b02d0336152bf36c + checksum: 2c89cc4630f569f9b6e2e0dcc29b168d3c14c730d5f04b2923b4eb0d517be327b22bf8b64eb50fb0412e2960f43ab0ccb4e99f6792ec409a9d24e7a2a85fa12f languageName: node linkType: hard @@ -5902,8 +5902,8 @@ __metadata: linkType: hard "docusaurus-plugin-openapi-docs@npm:^4.3.0": - version: 4.3.6 - resolution: "docusaurus-plugin-openapi-docs@npm:4.3.6" + version: 4.3.7 + resolution: "docusaurus-plugin-openapi-docs@npm:4.3.7" dependencies: "@apidevtools/json-schema-ref-parser": ^11.5.4 "@redocly/openapi-core": ^1.10.5 @@ -5925,7 +5925,7 @@ __metadata: "@docusaurus/utils": ^3.5.0 "@docusaurus/utils-validation": ^3.5.0 react: ^16.8.4 || ^17.0.0 || ^18.0.0 || ^19.0.0 - checksum: ed80d72f16cf03fc38cc9c6b9916287c80b4bf289040eacbd5a4bacabeed428a9293be974fb1bbede8595e2288820b7d86ca15aaf21366d043edf7e58c45aeae + checksum: 714d91c40ea00217ef654b690569dc3249a5048b83532da08be0c552e13f42b326cf1262490063519286e2cd30ad1c1bbd08f678d09c7341f945be9d8cd8e864 languageName: node linkType: hard @@ -11891,9 +11891,9 @@ __metadata: linkType: hard "prismjs@npm:^1.29.0": - version: 1.29.0 - resolution: "prismjs@npm:1.29.0" - checksum: 007a8869d4456ff8049dc59404e32d5666a07d99c3b0e30a18bd3b7676dfa07d1daae9d0f407f20983865fd8da56de91d09cb08e6aa61f5bc420a27c0beeaf93 + version: 1.30.0 + resolution: "prismjs@npm:1.30.0" + checksum: a68eddd4c5f1c506badb5434b0b28a7cc2479ed1df91bc4218e6833c7971ef40c50ec481ea49749ac964256acb78d8b66a6bd11554938e8998e46c18b5f9a580 languageName: node linkType: hard diff --git a/package.json b/package.json index 904ec6e229..920c67831a 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,6 @@ { "name": "root", - "version": "1.37.0-next.1", - "private": true, - "repository": { - "type": "git", - "url": "https://github.com/backstage/backstage" - }, + "version": "1.37.0-next.2", "backstage": { "cli": { "new": { @@ -16,6 +11,11 @@ } } }, + "private": true, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage" + }, "workspaces": { "packages": [ "packages/*", @@ -51,11 +51,11 @@ "snyk:test": "npx snyk test --yarn-workspaces --strict-out-of-sync=false", "snyk:test:package": "yarn snyk:test --include", "start": "yarn workspace example-app start", + "start-backend": "yarn workspace example-backend start", + "start-backend:legacy": "yarn workspace example-backend-legacy start", "start:lighthouse": "yarn workspaces foreach -A --include example-backend --include example-app --parallel --jobs unlimited -v -i run start", "start:microsite": "cd microsite/ && yarn start", "start:next": "yarn workspace example-app-next start", - "start-backend": "yarn workspace example-backend start", - "start-backend:legacy": "yarn workspace example-backend-legacy start", "storybook": "yarn ./storybook run storybook", "techdocs-cli": "node scripts/techdocs-cli.js", "techdocs-cli:dev": "cross-env TECHDOCS_CLI_DEV_MODE=true node scripts/techdocs-cli.js", @@ -103,9 +103,9 @@ "@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.0.0", "@types/react-dom": "^18.0.0", - "jest-haste-map@^29.7.0": "patch:jest-haste-map@npm%3A29.7.0#./.yarn/patches/jest-haste-map-npm-29.7.0-e3be419eff.patch", + "ast-types@0.14.2": "patch:ast-types@npm%3A0.14.2#./.yarn/patches/ast-types-npm-0.14.2-43c4ac4b0d.patch", "ast-types@^0.14.1": "patch:ast-types@npm%3A0.14.2#./.yarn/patches/ast-types-npm-0.14.2-43c4ac4b0d.patch", - "ast-types@0.14.2": "patch:ast-types@npm%3A0.14.2#./.yarn/patches/ast-types-npm-0.14.2-43c4ac4b0d.patch" + "jest-haste-map@^29.7.0": "patch:jest-haste-map@npm%3A29.7.0#./.yarn/patches/jest-haste-map-npm-29.7.0-e3be419eff.patch" }, "dependencies": { "@backstage/errors": "workspace:^", diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md index 2a3d24c2e2..3f2b8aa8cb 100644 --- a/packages/app-defaults/CHANGELOG.md +++ b/packages/app-defaults/CHANGELOG.md @@ -1,5 +1,47 @@ # @backstage/app-defaults +## 1.6.0-next.1 + +### Minor Changes + +- 12f8e01: **BREAKING**: The default `DiscoveryApi` implementation has been switched to use `FrontendHostDiscovery`, which adds support for the `discovery.endpoints` configuration. + + This is marked as a breaking change because it will cause any existing `discovery.endpoints` configuration to be picked up and used, which may break existing setups. + + For example, consider the following configuration: + + ```yaml + app: + baseUrl: https://backstage.acme.org + + backend: + baseUrl: https://backstage.internal.acme.org + + discovery: + endpoints: + - target: https://catalog.internal.acme.org/api/{{pluginId}} + plugins: [catalog] + ``` + + This will now cause requests from the frontend towards the `catalog` plugin to be routed to `https://catalog.internal.acme.org/api/catalog`, but this might not be reachable from the frontend. To fix this, you should update the `discovery.endpoints` configuration to only override the internal URL of the plugin: + + ```yaml + discovery: + endpoints: + - target: + internal: https://catalog.internal.acme.org/api/{{pluginId}} + plugins: [catalog] + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/theme@0.6.4 + - @backstage/plugin-permission-react@0.4.31 + ## 1.5.18-next.0 ### Patch Changes diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index 4c258b0348..88fbfc76d5 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/app-defaults", - "version": "1.5.18-next.0", + "version": "1.6.0-next.1", "description": "Provides the default wiring of a Backstage App", "backstage": { "role": "web-library" diff --git a/packages/app-defaults/src/defaults/apis.ts b/packages/app-defaults/src/defaults/apis.ts index 7fdbb78293..63ace7b4d5 100644 --- a/packages/app-defaults/src/defaults/apis.ts +++ b/packages/app-defaults/src/defaults/apis.ts @@ -28,13 +28,13 @@ import { BitbucketServerAuth, OAuthRequestManager, WebStorage, - UrlPatternDiscovery, OneLoginAuth, UnhandledErrorForwarder, AtlassianAuth, createFetchApi, FetchMiddlewares, VMwareCloudAuth, + FrontendHostDiscovery, } from '@backstage/core-app-api'; import { @@ -68,10 +68,7 @@ export const apis = [ createApiFactory({ api: discoveryApiRef, deps: { configApi: configApiRef }, - factory: ({ configApi }) => - UrlPatternDiscovery.compile( - `${configApi.getString('backend.baseUrl')}/api/{{ pluginId }}`, - ), + factory: ({ configApi }) => FrontendHostDiscovery.fromConfig(configApi), }), createApiFactory({ api: alertApiRef, diff --git a/packages/app-next-example-plugin/CHANGELOG.md b/packages/app-next-example-plugin/CHANGELOG.md index ee90447c5a..ae09a9215a 100644 --- a/packages/app-next-example-plugin/CHANGELOG.md +++ b/packages/app-next-example-plugin/CHANGELOG.md @@ -1,5 +1,13 @@ # app-next-example-plugin +## 0.0.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-components@0.16.5-next.1 + ## 0.0.21-next.1 ### Patch Changes diff --git a/packages/app-next-example-plugin/package.json b/packages/app-next-example-plugin/package.json index b27b237eac..6c192b11c8 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.21-next.1", + "version": "0.0.21-next.2", "description": "Backstage internal example plugin", "backstage": { "role": "frontend-plugin", diff --git a/packages/app-next/CHANGELOG.md b/packages/app-next/CHANGELOG.md index 5952e46836..85ce1ad698 100644 --- a/packages/app-next/CHANGELOG.md +++ b/packages/app-next/CHANGELOG.md @@ -1,5 +1,50 @@ # example-app-next +## 0.0.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.28.0-next.2 + - @backstage/frontend-app-api@0.11.0-next.2 + - @backstage/frontend-defaults@0.2.0-next.2 + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/cli@0.31.0-next.1 + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/plugin-signals@0.0.17-next.2 + - @backstage/plugin-app@0.1.7-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/plugin-scaffolder-react@1.14.6-next.2 + - @backstage/app-defaults@1.6.0-next.1 + - @backstage/plugin-scaffolder@1.29.0-next.2 + - @backstage/plugin-api-docs@0.12.5-next.2 + - @backstage/plugin-catalog-graph@0.4.17-next.2 + - @backstage/plugin-catalog-import@0.12.11-next.2 + - @backstage/plugin-org@0.6.37-next.2 + - @backstage/plugin-techdocs@1.12.4-next.2 + - @backstage/plugin-user-settings@0.8.20-next.2 + - @backstage/plugin-search-react@1.8.7-next.2 + - @backstage/plugin-app-visualizer@0.1.17-next.2 + - @backstage/plugin-catalog-unprocessed-entities@0.2.15-next.2 + - @backstage/plugin-home@0.8.6-next.2 + - @backstage/plugin-kubernetes@0.12.5-next.2 + - @backstage/plugin-notifications@0.5.3-next.2 + - @backstage/plugin-search@1.4.24-next.2 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.22-next.2 + - @backstage/plugin-techdocs-react@1.2.15-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/theme@0.6.4 + - @backstage/plugin-auth-react@0.1.13-next.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-kubernetes-cluster@0.0.23-next.2 + - @backstage/plugin-permission-react@0.4.31 + - @backstage/plugin-search-common@1.2.17 + ## 0.0.21-next.1 ### Patch Changes diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 9c4fa1b70a..2d1bdd0b70 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -1,6 +1,6 @@ { "name": "example-app-next", - "version": "0.0.21-next.1", + "version": "0.0.21-next.2", "backstage": { "role": "frontend" }, diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 559fcb33d7..60fc4ec936 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,46 @@ # example-app +## 0.2.107-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.28.0-next.2 + - @backstage/frontend-app-api@0.11.0-next.2 + - @backstage/cli@0.31.0-next.1 + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/plugin-signals@0.0.17-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/plugin-scaffolder-react@1.14.6-next.2 + - @backstage/app-defaults@1.6.0-next.1 + - @backstage/plugin-scaffolder@1.29.0-next.2 + - @backstage/plugin-api-docs@0.12.5-next.2 + - @backstage/plugin-catalog-graph@0.4.17-next.2 + - @backstage/plugin-catalog-import@0.12.11-next.2 + - @backstage/plugin-org@0.6.37-next.2 + - @backstage/plugin-techdocs@1.12.4-next.2 + - @backstage/plugin-user-settings@0.8.20-next.2 + - @backstage/plugin-search-react@1.8.7-next.2 + - @backstage/plugin-catalog-unprocessed-entities@0.2.15-next.2 + - @backstage/plugin-devtools@0.1.25-next.2 + - @backstage/plugin-home@0.8.6-next.2 + - @backstage/plugin-kubernetes@0.12.5-next.2 + - @backstage/plugin-notifications@0.5.3-next.2 + - @backstage/plugin-search@1.4.24-next.2 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.22-next.2 + - @backstage/plugin-techdocs-react@1.2.15-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/theme@0.6.4 + - @backstage/plugin-auth-react@0.1.13-next.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-kubernetes-cluster@0.0.23-next.2 + - @backstage/plugin-permission-react@0.4.31 + - @backstage/plugin-search-common@1.2.17 + ## 0.2.107-next.1 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 6d8125578a..9c082e832b 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "example-app", - "version": "0.2.107-next.1", + "version": "0.2.107-next.2", "backstage": { "role": "frontend" }, diff --git a/packages/backend-app-api/CHANGELOG.md b/packages/backend-app-api/CHANGELOG.md index 446c70f8dd..4bbc72c0fa 100644 --- a/packages/backend-app-api/CHANGELOG.md +++ b/packages/backend-app-api/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/backend-app-api +## 1.2.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.0-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-permission-node@0.8.9-next.1 + ## 1.2.1-next.1 ### Patch Changes diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index a5305cfa2c..c50e9155a7 100644 --- a/packages/backend-app-api/package.json +++ b/packages/backend-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-app-api", - "version": "1.2.1-next.1", + "version": "1.2.1-next.2", "description": "Core API used by Backstage backend apps", "backstage": { "role": "node-library" diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index babe55ab6f..f828e7bda3 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/backend-defaults +## 0.8.2-next.2 + +### Patch Changes + +- 12f8e01: The `discovery.endpoints` configuration no longer requires both `internal` and `external` target when using the object form, instead falling back to the default. +- Updated dependencies + - @backstage/config-loader@1.10.0-next.0 + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-app-api@1.2.1-next.2 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-permission-node@0.8.9-next.1 + ## 0.8.2-next.1 ### Patch Changes diff --git a/packages/backend-defaults/config.d.ts b/packages/backend-defaults/config.d.ts index 438790a177..ff8cf22164 100644 --- a/packages/backend-defaults/config.d.ts +++ b/packages/backend-defaults/config.d.ts @@ -652,7 +652,7 @@ export interface Config { * Can be either a string or an object with internal and external keys. * Targets with `{{pluginId}}` or `{{ pluginId }} in the URL will be replaced with the plugin ID. */ - target: string | { internal: string; external: string }; + target: string | { internal?: string; external?: string }; /** * Array of plugins which use the target base URL. */ diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index af54f9a7ad..3531b033bc 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-defaults", - "version": "0.8.2-next.1", + "version": "0.8.2-next.2", "description": "Backend defaults used by Backstage backend apps", "backstage": { "role": "node-library" diff --git a/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.test.ts b/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.test.ts index e4e30044dd..60ea9d7c3b 100644 --- a/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.test.ts +++ b/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.test.ts @@ -162,6 +162,42 @@ describe('HostDiscovery', () => { ); }); + it('allows plugin overrides to only override either internal or external targets', async () => { + const discovery = HostDiscovery.fromConfig( + new ConfigReader({ + backend: { + baseUrl: 'http://localhost:40', + listen: { port: 80, host: 'localhost' }, + }, + discovery: { + endpoints: [ + { + target: { internal: 'http://catalog-backend:8080/api/catalog' }, + plugins: ['catalog'], + }, + { + target: { external: 'http://frontend/api/scaffolder' }, + plugins: ['scaffolder'], + }, + ], + }, + }), + ); + + await expect(discovery.getBaseUrl('catalog')).resolves.toBe( + 'http://catalog-backend:8080/api/catalog', + ); + await expect(discovery.getExternalBaseUrl('catalog')).resolves.toBe( + 'http://localhost:40/api/catalog', + ); + await expect(discovery.getBaseUrl('scaffolder')).resolves.toBe( + 'http://localhost:80/api/scaffolder', + ); + await expect(discovery.getExternalBaseUrl('scaffolder')).resolves.toBe( + 'http://frontend/api/scaffolder', + ); + }); + it('replaces {{pluginId}} or {{ pluginId }} in the target', async () => { const discovery = HostDiscovery.fromConfig( new ConfigReader({ diff --git a/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts b/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts index b6c2c38d8a..214a347040 100644 --- a/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts +++ b/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts @@ -102,10 +102,13 @@ export class HostDiscovery implements DiscoveryService { private getTargetFromConfig(pluginId: string, type: 'internal' | 'external') { const endpoints = this.discoveryConfig?.getOptionalConfigArray('endpoints'); - const target = endpoints + const targetOrObj = endpoints ?.find(endpoint => endpoint.getStringArray('plugins').includes(pluginId)) ?.get('target'); + const target = + typeof targetOrObj === 'string' ? targetOrObj : targetOrObj?.[type]; + if (!target) { const baseUrl = type === 'external' ? this.externalBaseUrl : this.internalBaseUrl; @@ -113,14 +116,7 @@ export class HostDiscovery implements DiscoveryService { return `${baseUrl}/${encodeURIComponent(pluginId)}`; } - if (typeof target === 'string') { - return target.replace( - /\{\{\s*pluginId\s*\}\}/g, - encodeURIComponent(pluginId), - ); - } - - return target[type].replace( + return target.replace( /\{\{\s*pluginId\s*\}\}/g, encodeURIComponent(pluginId), ); diff --git a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.test.ts b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.test.ts index 1ce1401829..712ed63a2f 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.test.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.test.ts @@ -33,7 +33,7 @@ jest.setTimeout(60_000); describe('PluginTaskManagerImpl', () => { const addShutdownHook = jest.fn(); const databases = TestDatabases.create({ - ids: ['POSTGRES_16', 'POSTGRES_12', 'SQLITE_3'], + ids: ['POSTGRES_17', 'POSTGRES_13', 'SQLITE_3'], }); beforeAll(async () => { diff --git a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerJanitor.test.ts b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerJanitor.test.ts index 388f882afc..b2c6af24cb 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerJanitor.test.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerJanitor.test.ts @@ -39,8 +39,8 @@ describe('PluginTaskSchedulerJanitor', () => { const databases = TestDatabases.create({ ids: [ /* 'MYSQL_8' not supported yet */ - 'POSTGRES_16', - 'POSTGRES_12', + 'POSTGRES_17', + 'POSTGRES_13', 'SQLITE_3', 'MYSQL_8', ], diff --git a/packages/backend-defaults/src/setupTests.ts b/packages/backend-defaults/src/setupTests.ts index 76619a2542..c9745b280d 100644 --- a/packages/backend-defaults/src/setupTests.ts +++ b/packages/backend-defaults/src/setupTests.ts @@ -21,5 +21,5 @@ import { Settings } from 'luxon'; Settings.throwOnInvalid = true; TestDatabases.setDefaults({ - ids: ['MYSQL_8', 'POSTGRES_16', 'POSTGRES_12', 'SQLITE_3'], + ids: ['MYSQL_8', 'POSTGRES_17', 'POSTGRES_13', 'SQLITE_3'], }); diff --git a/packages/backend-dynamic-feature-service/CHANGELOG.md b/packages/backend-dynamic-feature-service/CHANGELOG.md index ee8aff4dee..9df8566d04 100644 --- a/packages/backend-dynamic-feature-service/CHANGELOG.md +++ b/packages/backend-dynamic-feature-service/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/backend-dynamic-feature-service +## 0.6.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/plugin-catalog-backend@1.32.0-next.2 + - @backstage/config-loader@1.10.0-next.0 + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/plugin-events-backend@0.4.4-next.2 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-app-node@0.1.31-next.2 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-node@0.8.9-next.1 + - @backstage/plugin-search-backend-node@1.3.9-next.1 + - @backstage/plugin-search-common@1.2.17 + ## 0.6.1-next.1 ### Patch Changes diff --git a/packages/backend-dynamic-feature-service/package.json b/packages/backend-dynamic-feature-service/package.json index 9d1d30876c..4a211b9cef 100644 --- a/packages/backend-dynamic-feature-service/package.json +++ b/packages/backend-dynamic-feature-service/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-dynamic-feature-service", - "version": "0.6.1-next.1", + "version": "0.6.1-next.2", "description": "Backstage dynamic feature service", "backstage": { "role": "node-library" diff --git a/packages/backend-legacy/CHANGELOG.md b/packages/backend-legacy/CHANGELOG.md index 09932a3f3a..987d64ebe5 100644 --- a/packages/backend-legacy/CHANGELOG.md +++ b/packages/backend-legacy/CHANGELOG.md @@ -1,5 +1,43 @@ # example-backend-legacy +## 0.2.108-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-gitlab@0.8.1-next.2 + - @backstage/plugin-scaffolder-backend@1.31.0-next.2 + - @backstage/plugin-catalog-backend@1.32.0-next.2 + - @backstage/plugin-techdocs-backend@1.11.7-next.2 + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-events-backend@0.4.4-next.2 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-auth-backend@0.24.4-next.2 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.6-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.6-next.1 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-kubernetes-backend@0.19.4-next.1 + - @backstage/plugin-permission-backend@0.5.55-next.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-node@0.8.9-next.1 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.7-next.2 + - @backstage/plugin-scaffolder-backend-module-rails@0.5.7-next.2 + - @backstage/plugin-search-backend@1.8.3-next.2 + - @backstage/plugin-search-backend-module-catalog@0.3.2-next.1 + - @backstage/plugin-search-backend-module-elasticsearch@1.6.6-next.1 + - @backstage/plugin-search-backend-module-explore@0.2.9-next.1 + - @backstage/plugin-search-backend-module-pg@0.5.42-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.3.7-next.2 + - @backstage/plugin-search-backend-node@1.3.9-next.1 + - @backstage/plugin-signals-backend@0.3.2-next.2 + - @backstage/plugin-signals-node@0.1.18-next.2 + ## 0.2.108-next.1 ### Patch Changes diff --git a/packages/backend-legacy/package.json b/packages/backend-legacy/package.json index 1fef7d42f2..a738f5c138 100644 --- a/packages/backend-legacy/package.json +++ b/packages/backend-legacy/package.json @@ -1,6 +1,6 @@ { "name": "example-backend-legacy", - "version": "0.2.108-next.1", + "version": "0.2.108-next.2", "backstage": { "role": "backend" }, diff --git a/packages/backend-legacy/src/index.ts b/packages/backend-legacy/src/index.ts index 507a1f2aee..1cfd62552f 100644 --- a/packages/backend-legacy/src/index.ts +++ b/packages/backend-legacy/src/index.ts @@ -25,6 +25,7 @@ import Router from 'express-promise-router'; import { CacheManager, + createLegacyAuthAdapters, createServiceBuilder, DatabaseManager, getRootLogger, @@ -37,7 +38,7 @@ import { import { Config } from '@backstage/config'; import healthcheck from './plugins/healthcheck'; import { metricsHandler, metricsInit } from './metrics'; -import auth from './plugins/auth'; +import authPlugin from './plugins/auth'; import catalog from './plugins/catalog'; import events from './plugins/events'; import kubernetes from './plugins/kubernetes'; @@ -59,10 +60,15 @@ function makeCreateEnv(config: Config) { const reader = UrlReaders.default({ logger: root, config }); const discovery = HostDiscovery.fromConfig(config); const tokenManager = ServerTokenManager.fromConfig(config, { logger: root }); - const permissions = ServerPermissionClient.fromConfig(config, { + const { auth } = createLegacyAuthAdapters({ + auth: undefined, discovery, tokenManager, }); + const permissions = ServerPermissionClient.fromConfig(config, { + discovery, + auth, + }); const databaseManager = DatabaseManager.fromConfig(config, { logger: root }); const cacheManager = CacheManager.fromConfig(config); const identity = DefaultIdentityClient.create({ @@ -137,7 +143,7 @@ async function main() { apiRouter.use('/catalog', await catalog(catalogEnv)); apiRouter.use('/events', await events(eventsEnv)); apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv)); - apiRouter.use('/auth', await auth(authEnv)); + apiRouter.use('/auth', await authPlugin(authEnv)); apiRouter.use('/search', await search(searchEnv)); apiRouter.use('/techdocs', await techdocs(techdocsEnv)); apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv)); diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index fb329ef956..1fd549147b 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/backend-test-utils +## 1.3.1-next.2 + +### Patch Changes + +- 37c6510: Moved `@types/jest` to `devDependencies`. +- Updated dependencies + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-app-api@1.2.1-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + ## 1.3.1-next.1 ### Patch Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 8141379bb4..6145147583 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": "1.3.1-next.1", + "version": "1.3.1-next.2", "description": "Test helpers library for Backstage backends", "backstage": { "role": "node-library" diff --git a/packages/backend-test-utils/report.api.md b/packages/backend-test-utils/report.api.md index 77fbba5ccf..89035cbaec 100644 --- a/packages/backend-test-utils/report.api.md +++ b/packages/backend-test-utils/report.api.md @@ -478,6 +478,7 @@ export class TestCaches { // @public export type TestDatabaseId = + | 'POSTGRES_17' | 'POSTGRES_16' | 'POSTGRES_15' | 'POSTGRES_14' diff --git a/packages/backend-test-utils/src/database/types.ts b/packages/backend-test-utils/src/database/types.ts index 39a8af065c..e1ef61a93b 100644 --- a/packages/backend-test-utils/src/database/types.ts +++ b/packages/backend-test-utils/src/database/types.ts @@ -28,6 +28,7 @@ export interface Engine { * @public */ export type TestDatabaseId = + | 'POSTGRES_17' | 'POSTGRES_16' | 'POSTGRES_15' | 'POSTGRES_14' @@ -47,6 +48,13 @@ export type TestDatabaseProperties = { export const allDatabases: Record = Object.freeze({ + POSTGRES_17: { + name: 'Postgres 17.x', + driver: 'pg', + dockerImageName: getDockerImageForName('postgres:17'), + connectionStringEnvironmentVariableName: + 'BACKSTAGE_TEST_DATABASE_POSTGRES17_CONNECTION_STRING', + }, POSTGRES_16: { name: 'Postgres 16.x', driver: 'pg', diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 1afeafe820..29da41eeb3 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,43 @@ # example-backend +## 0.0.36-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.31.0-next.2 + - @backstage/plugin-catalog-backend@1.32.0-next.2 + - @backstage/plugin-techdocs-backend@1.11.7-next.2 + - @backstage/plugin-scaffolder-backend-module-github@0.6.1-next.2 + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/plugin-events-backend@0.4.4-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/plugin-app-backend@0.5.0-next.2 + - @backstage/plugin-auth-backend@0.24.4-next.2 + - @backstage/plugin-auth-backend-module-github-provider@0.3.1-next.1 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.6-next.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.0-next.1 + - @backstage/plugin-catalog-backend-module-openapi@0.2.8-next.2 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.6-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.6-next.1 + - @backstage/plugin-devtools-backend@0.5.3-next.2 + - @backstage/plugin-kubernetes-backend@0.19.4-next.1 + - @backstage/plugin-notifications-backend@0.5.4-next.2 + - @backstage/plugin-permission-backend@0.5.55-next.1 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.6-next.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-node@0.8.9-next.1 + - @backstage/plugin-proxy-backend@0.6.0-next.1 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.8-next.2 + - @backstage/plugin-search-backend@1.8.3-next.2 + - @backstage/plugin-search-backend-module-catalog@0.3.2-next.1 + - @backstage/plugin-search-backend-module-explore@0.2.9-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.3.7-next.2 + - @backstage/plugin-search-backend-node@1.3.9-next.1 + - @backstage/plugin-signals-backend@0.3.2-next.2 + ## 0.0.36-next.1 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 9bc36762b3..51fabfb3a5 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.0.36-next.1", + "version": "0.0.36-next.2", "backstage": { "role": "backend" }, diff --git a/packages/canon/.gitignore b/packages/canon/.gitignore index e7c52ff306..b200b44847 100644 --- a/packages/canon/.gitignore +++ b/packages/canon/.gitignore @@ -2,4 +2,4 @@ storybook-static # CSS output -css +/css diff --git a/packages/canon/.storybook/preview.tsx b/packages/canon/.storybook/preview.tsx index e278bb011f..b1bfa2dc88 100644 --- a/packages/canon/.storybook/preview.tsx +++ b/packages/canon/.storybook/preview.tsx @@ -1,10 +1,7 @@ import React from 'react'; import type { Preview, ReactRenderer } from '@storybook/react'; import { withThemeByDataAttribute } from '@storybook/addon-themes'; - -// Canon specific styles -import '../src/css/core.css'; -import '../src/css/components.css'; +import '../src/css/styles.css'; const preview: Preview = { parameters: { diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index 6ff02d7437..69b689eb10 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -143,22 +143,12 @@ export const buttonPropDefs: { }; // @public -export interface ButtonProps { - // (undocumented) +export interface ButtonProps + extends Omit, 'children'> { children: React.ReactNode; - // (undocumented) - className?: string; - // (undocumented) - disabled?: boolean; - // (undocumented) iconEnd?: IconNames; - // (undocumented) iconStart?: IconNames; - // (undocumented) size?: ButtonOwnProps['size']; - // (undocumented) - style?: React.CSSProperties; - // (undocumented) variant?: ButtonOwnProps['variant']; } @@ -982,6 +972,19 @@ export interface TextProps { // (undocumented) className?: string; // (undocumented) + color?: + | 'primary' + | 'secondary' + | 'danger' + | 'warning' + | 'success' + | Partial< + Record< + Breakpoint, + 'primary' | 'secondary' | 'danger' | 'warning' | 'success' + > + >; + // (undocumented) style?: CSSProperties; // (undocumented) variant?: diff --git a/packages/canon/scripts/build-css.mjs b/packages/canon/scripts/build-css.mjs index 4ea38f8085..e0ba8378a9 100644 --- a/packages/canon/scripts/build-css.mjs +++ b/packages/canon/scripts/build-css.mjs @@ -31,6 +31,7 @@ const componentsDir = 'src/components'; const cssFiles = [ { path: `${cssDir}/core.css`, newName: 'core.css' }, { path: `${cssDir}/components.css`, newName: 'components.css' }, + { path: `${cssDir}/styles.css`, newName: 'styles.css' }, ]; // Components files diff --git a/packages/canon/src/components/Button/styles.css b/packages/canon/src/components/Button/styles.css index 0572028e96..b5d6e1dca0 100644 --- a/packages/canon/src/components/Button/styles.css +++ b/packages/canon/src/components/Button/styles.css @@ -57,7 +57,7 @@ } .canon-Button--variant-secondary { - background-color: var(--canon-bg-elevated); + background-color: var(--canon-bg-surface-1); box-shadow: inset 0 0 0 1px var(--canon-border); color: var(--canon-fg-primary); transition: box-shadow 150ms ease; diff --git a/packages/canon/src/components/Button/types.ts b/packages/canon/src/components/Button/types.ts index 89bb8c79e5..0d61e1cda4 100644 --- a/packages/canon/src/components/Button/types.ts +++ b/packages/canon/src/components/Button/types.ts @@ -21,13 +21,32 @@ import type { ButtonOwnProps } from './Button.props'; * * @public */ -export interface ButtonProps { +export interface ButtonProps + extends Omit, 'children'> { + /** + * The size of the button + * @defaultValue 'medium' + */ size?: ButtonOwnProps['size']; + + /** + * The visual variant of the button + * @defaultValue 'primary' + */ variant?: ButtonOwnProps['variant']; + + /** + * The content of the button + */ children: React.ReactNode; - className?: string; - disabled?: boolean; + + /** + * Optional icon to display at the start of the button + */ iconStart?: IconNames; + + /** + * Optional icon to display at the end of the button + */ iconEnd?: IconNames; - style?: React.CSSProperties; } diff --git a/packages/canon/src/components/Input/Input.styles.css b/packages/canon/src/components/Input/Input.styles.css index 4e3e6771ab..0ff108008d 100644 --- a/packages/canon/src/components/Input/Input.styles.css +++ b/packages/canon/src/components/Input/Input.styles.css @@ -18,7 +18,7 @@ border-radius: var(--canon-radius-3); border: 1px solid var(--canon-border); padding: 0 var(--canon-space-4); - background-color: var(--canon-bg-elevated); + background-color: var(--canon-bg-surface-1); font-size: var(--canon-font-size-3); font-family: var(--canon-font-regular); font-weight: var(--canon-font-weight-regular); diff --git a/packages/canon/src/components/Link/Link.stories.tsx b/packages/canon/src/components/Link/Link.stories.tsx new file mode 100644 index 0000000000..38e71c310a --- /dev/null +++ b/packages/canon/src/components/Link/Link.stories.tsx @@ -0,0 +1,92 @@ +/* + * 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 type { Meta, StoryObj } from '@storybook/react'; +import { Link } from './Link'; +import { Flex } from '../Flex'; +import { Text } from '../Text'; + +const meta = { + title: 'Components/Link', + component: Link, + args: { + children: 'Link', + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + href: 'https://canon.backstage.io', + children: 'Sign up for Backstage', + }, +}; + +export const AllVariants: Story = { + args: { + ...Default.args, + }, + render: args => ( + + + + + + + ), +}; + +export const AllWeights: Story = { + args: { + ...Default.args, + }, + render: args => ( + + + + + ), +}; + +export const Responsive: Story = { + args: { + variant: { + xs: 'label', + md: 'body', + }, + }, +}; + +export const Playground: Story = { + args: { + ...Default.args, + }, + render: args => ( + + Subtitle + + Body + + Caption + + Label + + + ), +}; diff --git a/packages/canon/src/components/Link/Link.tsx b/packages/canon/src/components/Link/Link.tsx new file mode 100644 index 0000000000..8763575bf9 --- /dev/null +++ b/packages/canon/src/components/Link/Link.tsx @@ -0,0 +1,55 @@ +/* + * 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, { forwardRef } from 'react'; +import { useResponsiveValue } from '../../hooks/useResponsiveValue'; +import clsx from 'clsx'; + +import type { LinkProps } from './types'; + +/** @public */ +export const Link = forwardRef((props, ref) => { + const { + children, + variant = 'body', + weight = 'regular', + style, + className, + ...restProps + } = props; + + // Get the responsive values for the variant and weight + const responsiveVariant = useResponsiveValue(variant); + const responsiveWeight = useResponsiveValue(weight); + + return ( + + {children} + + ); +}); + +Link.displayName = 'Link'; diff --git a/packages/frontend-test-utils/src/deprecated.ts b/packages/canon/src/components/Link/index.ts similarity index 63% rename from packages/frontend-test-utils/src/deprecated.ts rename to packages/canon/src/components/Link/index.ts index 1b67f73e6b..1b38e89d62 100644 --- a/packages/frontend-test-utils/src/deprecated.ts +++ b/packages/canon/src/components/Link/index.ts @@ -14,16 +14,5 @@ * limitations under the License. */ -import { registerMswTestHooks } from '@backstage/test-utils'; - -/** - * @public - * @deprecated Use `registerMswTestHooks` from `@backstage/frontend-test-utils` instead. - */ -export function setupRequestMockHandlers(worker: { - listen: (t: any) => void; - close: () => void; - resetHandlers: () => void; -}): void { - registerMswTestHooks(worker); -} +export { Link } from './Link'; +export type { LinkProps } from './types'; diff --git a/packages/canon/src/components/Link/styles.css b/packages/canon/src/components/Link/styles.css new file mode 100644 index 0000000000..40a4f340ca --- /dev/null +++ b/packages/canon/src/components/Link/styles.css @@ -0,0 +1,65 @@ +/* + * 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. + */ + +.canon-Link { + font-family: var(--canon-font-regular); + color: var(--canon-fg-link); + padding: 0; + margin: 0; + cursor: pointer; + text-decoration-line: none; + + &:hover { + color: var(--canon-fg-link-hover); + text-decoration-line: underline; + text-decoration-style: solid; + text-decoration-thickness: min(2px, max(1px, 0.05em)); + text-underline-offset: calc(0.025em + 2px); + text-decoration-color: color-mix( + in srgb, + var(--canon-fg-link-hover) 30%, + transparent + ); + } +} + +.canon-Link--variant-body { + font-size: var(--canon-font-size-3); + line-height: 140%; +} + +.canon-Link--variant-subtitle { + font-size: var(--canon-font-size-4); + line-height: 140%; +} + +.canon-Link--variant-caption { + font-size: var(--canon-font-size-2); + line-height: 140%; +} + +.canon-Link--variant-label { + font-size: var(--canon-font-size-1); + line-height: 140%; +} + +.canon-Link--weight-regular { + font-weight: var(--canon-font-weight-regular); +} + +.canon-Link--weight-bold { + font-weight: var(--canon-font-weight-bold); +} diff --git a/packages/canon/src/components/Link/types.ts b/packages/canon/src/components/Link/types.ts new file mode 100644 index 0000000000..4c5b34e14e --- /dev/null +++ b/packages/canon/src/components/Link/types.ts @@ -0,0 +1,33 @@ +/* + * 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 type { CSSProperties, ReactNode } from 'react'; +import type { Breakpoint } from '../../types'; + +/** @public */ +export interface LinkProps + extends React.AnchorHTMLAttributes { + children: ReactNode; + variant?: + | 'subtitle' + | 'body' + | 'caption' + | 'label' + | Partial>; + weight?: 'regular' | 'bold' | Partial>; + className?: string; + style?: CSSProperties; +} diff --git a/packages/canon/src/components/Table/styles.css b/packages/canon/src/components/Table/styles.css index b834485ff5..7d3cb35e3d 100644 --- a/packages/canon/src/components/Table/styles.css +++ b/packages/canon/src/components/Table/styles.css @@ -2,7 +2,7 @@ position: relative; overflow: auto; width: 100%; - background-color: var(--canon-bg-elevated); + background-color: var(--canon-bg-surface-1); border-radius: var(--canon-radius-2); padding-bottom: var(--canon-space-0_5); padding-top: var(--canon-space-0_5); @@ -39,16 +39,16 @@ & .table-cell:first-child { border-top-left-radius: var(--canon-radius-2); border-bottom-left-radius: var(--canon-radius-2); - box-shadow: inset 4px 2px 0 0 var(--canon-bg-elevated), - inset 4px -2px 0 0 var(--canon-bg-elevated); + box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1), + inset 4px -2px 0 0 var(--canon-bg-surface-1); padding-left: var(--canon-space-3); } & .table-cell:last-child { border-top-right-radius: var(--canon-radius-2); border-bottom-right-radius: var(--canon-radius-2); - box-shadow: inset -4px 2px 0 0 var(--canon-bg-elevated), - inset -4px -2px 0 0 var(--canon-bg-elevated); + box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1), + inset -4px -2px 0 0 var(--canon-bg-surface-1); padding-right: var(--canon-space-3); } } @@ -56,6 +56,6 @@ .table-cell { padding: var(--canon-space-3); background-color: var(--canon-bg); - box-shadow: inset 0px 2px 0 0 var(--canon-bg-elevated), - inset 0px -2px 0 0 var(--canon-bg-elevated); + box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1), + inset 0px -2px 0 0 var(--canon-bg-surface-1); } diff --git a/packages/canon/src/components/Text/Text.stories.tsx b/packages/canon/src/components/Text/Text.stories.tsx index 547b637619..9cf2a73f6e 100644 --- a/packages/canon/src/components/Text/Text.stories.tsx +++ b/packages/canon/src/components/Text/Text.stories.tsx @@ -39,51 +39,49 @@ export const Default: Story = { }; export const AllVariants: Story = { - render: () => ( - - - A man looks at a painting in a museum and says, “Brothers and sisters I - have none, but that man's father is my father's son.” Who is - in the painting? - - - A man looks at a painting in a museum and says, “Brothers and sisters I - have none, but that man's father is my father's son.” Who is - in the painting? - - - A man looks at a painting in a museum and says, “Brothers and sisters I - have none, but that man's father is my father's son.” Who is - in the painting? - - - A man looks at a painting in a museum and says, “Brothers and sisters I - have none, but that man's father is my father's son.” Who is - in the painting? - + args: { + ...Default.args, + }, + render: args => ( + + + + + ), }; export const AllWeights: Story = { - render: () => ( - - - A man looks at a painting in a museum and says, “Brothers and sisters I - have none, but that man's father is my father's son.” Who is - in the painting? - - - A man looks at a painting in a museum and says, “Brothers and sisters I - have none, but that man's father is my father's son.” Who is - in the painting? - + args: { + ...Default.args, + }, + render: args => ( + + + + + ), +}; + +export const AllColors: Story = { + args: { + ...Default.args, + }, + render: args => ( + + + + + + ), }; export const Responsive: Story = { args: { + ...Default.args, variant: { xs: 'label', md: 'body', @@ -93,7 +91,7 @@ export const Responsive: Story = { export const Playground: Story = { render: () => ( - + Subtitle A man looks at a painting in a museum and says, “Brothers and sisters I diff --git a/packages/canon/src/components/Text/Text.tsx b/packages/canon/src/components/Text/Text.tsx index 1d490bc0ef..8bc1daf465 100644 --- a/packages/canon/src/components/Text/Text.tsx +++ b/packages/canon/src/components/Text/Text.tsx @@ -27,6 +27,7 @@ export const Text = forwardRef( children, variant = 'body', weight = 'regular', + color = 'primary', style, className, ...restProps @@ -35,6 +36,7 @@ export const Text = forwardRef( // Get the responsive values for the variant and weight const responsiveVariant = useResponsiveValue(variant); const responsiveWeight = useResponsiveValue(weight); + const responsiveColor = useResponsiveValue(color); return (

( 'canon-Text', responsiveVariant && `canon-Text--variant-${responsiveVariant}`, responsiveWeight && `canon-Text--weight-${responsiveWeight}`, + responsiveColor && `canon-Text--color-${responsiveColor}`, className, )} style={style} diff --git a/packages/canon/src/components/Text/styles.css b/packages/canon/src/components/Text/styles.css index 871960b8db..8252554280 100644 --- a/packages/canon/src/components/Text/styles.css +++ b/packages/canon/src/components/Text/styles.css @@ -48,3 +48,23 @@ .canon-Text--weight-bold { font-weight: var(--canon-font-weight-bold); } + +.canon-Text--color-primary { + color: var(--canon-fg-primary); +} + +.canon-Text--color-secondary { + color: var(--canon-fg-secondary); +} + +.canon-Text--color-danger { + color: var(--canon-fg-danger); +} + +.canon-Text--color-warning { + color: var(--canon-fg-warning); +} + +.canon-Text--color-success { + color: var(--canon-fg-success); +} diff --git a/packages/canon/src/components/Text/types.ts b/packages/canon/src/components/Text/types.ts index 4fdc824120..d4f1615694 100644 --- a/packages/canon/src/components/Text/types.ts +++ b/packages/canon/src/components/Text/types.ts @@ -27,6 +27,18 @@ export interface TextProps { | 'label' | Partial>; weight?: 'regular' | 'bold' | Partial>; + color?: + | 'primary' + | 'secondary' + | 'danger' + | 'warning' + | 'success' + | Partial< + Record< + Breakpoint, + 'primary' | 'secondary' | 'danger' | 'warning' | 'success' + > + >; className?: string; style?: CSSProperties; } diff --git a/packages/canon/src/css/components.css b/packages/canon/src/css/components.css index 40b791a83f..3361c33248 100644 --- a/packages/canon/src/css/components.css +++ b/packages/canon/src/css/components.css @@ -14,7 +14,6 @@ * limitations under the License. */ -/* Components */ @import '../components/Box/styles.css'; @import '../components/Button/styles.css'; @import '../components/Flex/styles.css'; @@ -28,3 +27,4 @@ @import '../components/Input/Input.styles.css'; @import '../components/Field/Field.styles.css'; @import '../components/Menu/Menu.styles.css'; +@import '../components/Link/styles.css'; diff --git a/packages/canon/src/css/core.css b/packages/canon/src/css/core.css index e1b3baf0ca..8c7863953a 100644 --- a/packages/canon/src/css/core.css +++ b/packages/canon/src/css/core.css @@ -72,7 +72,8 @@ /* Background Colors */ --canon-bg: #f8f8f8; - --canon-bg-elevated: #fff; + --canon-bg-surface-1: #fff; + --canon-bg-surface-2: #ececec; --canon-bg-solid: #1f5493; --canon-bg-solid-hover: #163a66; --canon-bg-solid-pressed: #0f2b4e; @@ -116,7 +117,8 @@ [data-theme='dark'] { /* Background Colors */ --canon-bg: #000000; - --canon-bg-elevated: #191919; + --canon-bg-surface-1: #191919; + --canon-bg-surface-2: #242424; --canon-bg-solid: #9cc9ff; --canon-bg-solid-hover: #83b9fd; --canon-bg-solid-pressed: #83b9fd; diff --git a/packages/canon/src/css/styles.css b/packages/canon/src/css/styles.css new file mode 100644 index 0000000000..f5d623389f --- /dev/null +++ b/packages/canon/src/css/styles.css @@ -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. + */ + +@import './core.css'; +@import './components.css'; diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 145eaa5d3e..76c1af75bb 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/cli +## 0.31.0-next.1 + +### Minor Changes + +- 5b70679: **BREAKING**: ESLint warnings no longer trigger system exit codes like errors do. + + Set the max number of warnings to `-1` during linting to enable the gradual adoption of new ESLint rules. To restore the previous behavior, include the `--max-warnings 0` flag in the `backstage-cli lint` command. + +### Patch Changes + +- e0b226b: build(deps): bump `esbuild` from 0.24.2 to 0.25.0 +- 4d45498: Fixed the package prepack command so that it no longer produces unnecessary `index` entries in the `typesVersions` map, which could cause `/index` to be added when automatically adding imports. +- f8bd342: Fix a bug in the translation of the deprecated `--scope` option for the `new` command that could cause plugins to have `backstage-backstage-plugin` in their name. +- Updated dependencies + - @backstage/config-loader@1.10.0-next.0 + - @backstage/integration@1.16.2-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/eslint-plugin@0.1.10 + - @backstage/release-manifests@0.0.12 + - @backstage/types@1.2.1 + ## 0.30.1-next.0 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 6406034c44..dec6fed874 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli", - "version": "0.30.1-next.0", + "version": "0.31.0-next.1", "description": "CLI for developing Backstage plugins and apps", "backstage": { "role": "cli" diff --git a/packages/cli/src/alpha.ts b/packages/cli/src/alpha.ts index d6189a4256..49e739bbdc 100644 --- a/packages/cli/src/alpha.ts +++ b/packages/cli/src/alpha.ts @@ -29,5 +29,6 @@ import chalk from 'chalk'; initializer.add(import('./modules/build/alpha')); initializer.add(import('./modules/migrate/alpha')); initializer.add(import('./modules/test/alpha')); + initializer.add(import('./modules/lint/alpha')); await initializer.run(); })(); diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index ee73c071f8..9a76711d08 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -31,6 +31,10 @@ import { registerRepoCommands as registerRepoTestCommands, registerPackageCommands as registerPackageTestCommands, } from '../modules/test'; +import { + registerPackageCommands as registerPackageLintCommands, + registerRepoCommands as registerRepoLintCommands, +} from '../modules/lint'; export function registerRepoCommand(program: Command) { const command = program @@ -39,33 +43,7 @@ export function registerRepoCommand(program: Command) { registerRepoBuildCommands(command); registerRepoTestCommands(command); - - command - .command('lint') - .description('Lint all packages in the project') - .option( - '--format ', - 'Lint report output format', - 'eslint-formatter-friendly', - ) - .option( - '--output-file ', - 'Write the lint report to a file instead of stdout', - ) - .option( - '--since ', - 'Only lint packages that changed since the specified ref', - ) - .option( - '--successCache', - 'Enable success caching, which skips running tests for unchanged packages that were successful in the previous run', - ) - .option( - '--successCacheDir ', - 'Set the success cache location, (default: node_modules/.cache/backstage-cli)', - ) - .option('--fix', 'Attempt to automatically fix violations') - .action(lazy(() => import('./repo/lint'), 'command')); + registerRepoLintCommands(command); command .command('fix') @@ -115,25 +93,7 @@ export function registerScriptCommand(program: Command) { registerPackageBuildCommands(command); registerPackageTestCommands(command); - command - .command('lint [directories...]') - .option( - '--format ', - 'Lint report output format', - 'eslint-formatter-friendly', - ) - .option( - '--output-file ', - 'Write the lint report to a file instead of stdout', - ) - .option('--fix', 'Attempt to automatically fix violations') - .option( - '--max-warnings ', - 'Fail if more than this number of warnings. -1 allows warnings. (default: -1)', - ) - .description('Lint a package') - .action(lazy(() => import('./lint'), 'default')); - + registerPackageLintCommands(command); command .command('clean') .description('Delete cache directories') diff --git a/packages/cli/src/modules/lint/alpha.ts b/packages/cli/src/modules/lint/alpha.ts new file mode 100644 index 0000000000..8703e73b5e --- /dev/null +++ b/packages/cli/src/modules/lint/alpha.ts @@ -0,0 +1,86 @@ +/* + * 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 { createCliPlugin } from '../../wiring/factory'; +import { Command } from 'commander'; +import { lazy } from '../../lib/lazy'; + +export default createCliPlugin({ + pluginId: 'lint', + init: async reg => { + reg.addCommand({ + path: ['package', 'lint'], + description: 'Lint a package', + execute: async ({ args }) => { + const command = new Command(); + command.arguments('[directories...]'); + command.option('--fix', 'Attempt to automatically fix violations'); + command.option( + '--format ', + 'Lint report output format', + 'eslint-formatter-friendly', + ); + command.option( + '--output-file ', + 'Write the lint report to a file instead of stdout', + ); + command.option( + '--max-warnings ', + 'Fail if more than this number of warnings. -1 allows warnings. (default: 0)', + ); + command.description('Lint a package'); + command.action( + lazy(() => import('./commands/package/lint'), 'default'), + ); + + await command.parseAsync(args, { from: 'user' }); + }, + }); + + reg.addCommand({ + path: ['repo', 'lint'], + description: 'Lint a repository', + execute: async ({ args }) => { + const command = new Command(); + command.option('--fix', 'Attempt to automatically fix violations'); + command.option( + '--format ', + 'Lint report output format', + 'eslint-formatter-friendly', + ); + command.option( + '--output-file ', + 'Write the lint report to a file instead of stdout', + ); + command.option( + '--successCache', + 'Enable success caching, which skips running tests for unchanged packages that were successful in the previous run', + ); + command.option( + '--successCacheDir ', + 'Set the success cache location, (default: node_modules/.cache/backstage-cli)', + ); + command.option( + '--since ', + 'Only lint packages that changed since the specified ref', + ); + command.description('Lint a repository'); + command.action(lazy(() => import('./commands/repo/lint'), 'command')); + + await command.parseAsync(args, { from: 'user' }); + }, + }); + }, +}); diff --git a/packages/cli/src/commands/lint.ts b/packages/cli/src/modules/lint/commands/package/lint.ts similarity index 97% rename from packages/cli/src/commands/lint.ts rename to packages/cli/src/modules/lint/commands/package/lint.ts index cd5c265e72..a1e45c0113 100644 --- a/packages/cli/src/commands/lint.ts +++ b/packages/cli/src/modules/lint/commands/package/lint.ts @@ -16,7 +16,7 @@ import fs from 'fs-extra'; import { OptionValues } from 'commander'; -import { paths } from '../lib/paths'; +import { paths } from '../../../../lib/paths'; import { ESLint } from 'eslint'; export default async (directories: string[], opts: OptionValues) => { diff --git a/packages/cli/src/commands/repo/lint.ts b/packages/cli/src/modules/lint/commands/repo/lint.ts similarity index 96% rename from packages/cli/src/commands/repo/lint.ts rename to packages/cli/src/modules/lint/commands/repo/lint.ts index f78b51ea5d..15a3584020 100644 --- a/packages/cli/src/commands/repo/lint.ts +++ b/packages/cli/src/modules/lint/commands/repo/lint.ts @@ -24,10 +24,10 @@ import { BackstagePackageJson, Lockfile, } from '@backstage/cli-node'; -import { paths } from '../../lib/paths'; -import { runWorkerQueueThreads } from '../../lib/parallel'; -import { createScriptOptionsParser } from './optionsParser'; -import { SuccessCache } from '../../lib/cache/SuccessCache'; +import { paths } from '../../../../lib/paths'; +import { runWorkerQueueThreads } from '../../../../lib/parallel'; +import { createScriptOptionsParser } from '../../../../commands/repo/optionsParser'; +import { SuccessCache } from '../../../../lib/cache/SuccessCache'; function depCount(pkg: BackstagePackageJson) { const deps = pkg.dependencies ? Object.keys(pkg.dependencies).length : 0; diff --git a/packages/cli/src/modules/lint/index.ts b/packages/cli/src/modules/lint/index.ts new file mode 100644 index 0000000000..e2bfcd0779 --- /dev/null +++ b/packages/cli/src/modules/lint/index.ts @@ -0,0 +1,67 @@ +/* + * 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 { Command } from 'commander'; +import { lazy } from '../../lib/lazy'; + +export function registerPackageCommands(command: Command) { + command + .command('lint [directories...]') + .option( + '--format ', + 'Lint report output format', + 'eslint-formatter-friendly', + ) + .option( + '--output-file ', + 'Write the lint report to a file instead of stdout', + ) + .option('--fix', 'Attempt to automatically fix violations') + .option( + '--max-warnings ', + 'Fail if more than this number of warnings. -1 allows warnings. (default: 0)', + ) + .description('Lint a package') + .action(lazy(() => import('./commands/package/lint'), 'default')); +} + +export function registerRepoCommands(command: Command) { + command + .command('lint') + .description('Lint all packages in the project') + .option( + '--format ', + 'Lint report output format', + 'eslint-formatter-friendly', + ) + .option( + '--output-file ', + 'Write the lint report to a file instead of stdout', + ) + .option( + '--since ', + 'Only lint packages that changed since the specified ref', + ) + .option( + '--successCache', + 'Enable success caching, which skips running tests for unchanged packages that were successful in the previous run', + ) + .option( + '--successCacheDir ', + 'Set the success cache location, (default: node_modules/.cache/backstage-cli)', + ) + .option('--fix', 'Attempt to automatically fix violations') + .action(lazy(() => import('./commands/repo/lint'), 'command')); +} diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index 7e64bab192..5a451559ed 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/config-loader +## 1.10.0-next.0 + +### Minor Changes + +- 2fd73aa: The include transforms applied during config loading will now only apply to the known keys `$file`, `$env`, and `$include`. Any other key that begins with a `# @backstage/config-loader will now be passed through as is. + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 1.9.6 ### Patch Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 421c30db0c..37d4e94e44 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/config-loader", - "version": "1.9.6", + "version": "1.10.0-next.0", "description": "Config loading functionality used by Backstage backend, and CLI", "backstage": { "role": "node-library" diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index e5b32c385f..ef8e52394f 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/core-app-api +## 1.16.0-next.0 + +### Minor Changes + +- 9262001: The default auth injection middleware for the `FetchApi` will now also take configuration under `discovery.endpoints` into consideration when deciding whether to include credentials or not. +- 12f8e01: The `discovery.endpoints` configuration no longer requires both `internal` and `external` target when using the object form, instead falling back to the default. + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + ## 1.15.5 ### Patch Changes diff --git a/packages/core-app-api/config.d.ts b/packages/core-app-api/config.d.ts index 1a7ccb666f..18519bc291 100644 --- a/packages/core-app-api/config.d.ts +++ b/packages/core-app-api/config.d.ts @@ -163,7 +163,7 @@ export interface Config { /** * @visibility frontend */ - external: string; + external?: string; }; /** * Array of plugins which use the target baseUrl. diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 76c3632b65..eb61af575f 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-app-api", - "version": "1.15.5", + "version": "1.16.0-next.0", "description": "Core app API used by Backstage apps", "backstage": { "role": "web-library" diff --git a/packages/core-app-api/src/apis/implementations/DiscoveryApi/FrontendHostDiscovery.test.ts b/packages/core-app-api/src/apis/implementations/DiscoveryApi/FrontendHostDiscovery.test.ts index 0ba6457a1b..39e7ec7fab 100644 --- a/packages/core-app-api/src/apis/implementations/DiscoveryApi/FrontendHostDiscovery.test.ts +++ b/packages/core-app-api/src/apis/implementations/DiscoveryApi/FrontendHostDiscovery.test.ts @@ -72,6 +72,30 @@ describe('FrontendHostDiscovery', () => { ); }); + it('should not use internal plugin overrides', async () => { + const discovery = FrontendHostDiscovery.fromConfig( + new ConfigReader({ + backend: { + baseUrl: 'http://localhost:40', + }, + discovery: { + endpoints: [ + { + target: { + internal: 'http://catalog-backend-internal:8080/api/catalog', + }, + plugins: ['catalog'], + }, + ], + }, + }), + ); + + await expect(discovery.getBaseUrl('catalog')).resolves.toBe( + 'http://localhost:40/api/catalog', + ); + }); + it('uses a single target for internal and external for a plugin', async () => { const discovery = FrontendHostDiscovery.fromConfig( new ConfigReader({ diff --git a/packages/core-app-api/src/apis/implementations/DiscoveryApi/FrontendHostDiscovery.ts b/packages/core-app-api/src/apis/implementations/DiscoveryApi/FrontendHostDiscovery.ts index 950b746684..9c10c386d6 100644 --- a/packages/core-app-api/src/apis/implementations/DiscoveryApi/FrontendHostDiscovery.ts +++ b/packages/core-app-api/src/apis/implementations/DiscoveryApi/FrontendHostDiscovery.ts @@ -54,8 +54,11 @@ export class FrontendHostDiscovery implements DiscoveryApi { ?.flatMap(e => { const target = typeof e.get('target') === 'object' - ? e.getString('target.external') + ? e.getOptionalString('target.external') : e.getString('target'); + if (!target) { + return []; + } const discovery = UrlPatternDiscovery.compile(target); return e .getStringArray('plugins') diff --git a/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.test.ts b/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.test.ts index 50f93444ed..7d18325540 100644 --- a/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.test.ts +++ b/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.test.ts @@ -115,4 +115,49 @@ describe('IdentityAuthInjectorFetchMiddleware', () => { ['authorization', 'do-not-clobber'], ]); }); + + describe('.getDiscoveryUrlPrefixes', () => { + it('works with no endpoints', () => { + const config = new ConfigReader({ + backend: { baseUrl: 'https://a.com' }, + }); + expect( + IdentityAuthInjectorFetchMiddleware.getDiscoveryUrlPrefixes(config), + ).toEqual([]); + }); + + it('works with endpoints', () => { + const config = new ConfigReader({ + backend: { baseUrl: 'https://a.com' }, + discovery: { + endpoints: [ + { target: 'https://b.com', plugins: ['p1'] }, + { target: 'https://c.com/{{pluginId}}', plugins: ['p2', 'p3'] }, + { target: { external: 'https://d.com' }, plugins: ['q1'] }, + { + target: { external: 'https://e.com/{{pluginId}}' }, + plugins: ['q2', 'q3'], + }, + { + target: { + external: 'https://{{ pluginId }}.e.com/{{pluginId}}', + }, + plugins: ['q4'], + }, + ], + }, + }); + expect( + IdentityAuthInjectorFetchMiddleware.getDiscoveryUrlPrefixes(config), + ).toEqual([ + 'https://b.com', + 'https://c.com/p2', + 'https://c.com/p3', + 'https://d.com', + 'https://e.com/q2', + 'https://e.com/q3', + 'https://q4.e.com/q4', + ]); + }); + }); }); diff --git a/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.ts b/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.ts index 6f9733cba1..842f9191eb 100644 --- a/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.ts +++ b/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.ts @@ -45,6 +45,25 @@ export class IdentityAuthInjectorFetchMiddleware implements FetchMiddleware { ); } + /** + * Returns an array of plugin URL prefixes derived from the static `discovery` + * configuration, to be used as `urlPrefixAllowlist` option of {@link create}. + */ + static getDiscoveryUrlPrefixes(config: Config): string[] { + const endpointConfigs = + config.getOptionalConfigArray('discovery.endpoints') || []; + return endpointConfigs.flatMap(c => { + const target = + typeof c.get('target') === 'object' + ? c.getString('target.external') + : c.getString('target'); + const plugins = c.getStringArray('plugins'); + return plugins.map(pluginId => + target.replace(/\{\{\s*pluginId\s*\}\}/g, pluginId), + ); + }); + } + constructor( public readonly identityApi: IdentityApi, public readonly allowUrl: (url: string) => boolean, @@ -87,7 +106,12 @@ function buildMatcher(options: { } else if (options.urlPrefixAllowlist) { return buildPrefixMatcher(options.urlPrefixAllowlist); } else if (options.config) { - return buildPrefixMatcher([options.config.getString('backend.baseUrl')]); + return buildPrefixMatcher([ + options.config.getString('backend.baseUrl'), + ...IdentityAuthInjectorFetchMiddleware.getDiscoveryUrlPrefixes( + options.config, + ), + ]); } return () => false; } diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index 94f5bd638b..4580cdc7aa 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/core-compat-api +## 0.4.0-next.2 + +### Minor Changes + +- 8250ffe: **BREAKING**: Dropped support for the removed opaque `@backstage/ExtensionOverrides` and `@backstage/BackstagePlugin` types. + +### Patch Changes + +- e7fab55: Added the `entityPage` option to `convertLegacyApp`, which you can read more about in the [app migration docs](https://backstage.io/docs/frontend-system/building-apps/migrating#entity-pages). +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/version-bridge@1.0.11 + ## 0.3.7-next.1 ### Patch Changes diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index d49cb9bdaa..0db4872230 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.3.7-next.1", + "version": "0.4.0-next.2", "backstage": { "role": "web-library" }, @@ -33,6 +33,7 @@ "dependencies": { "@backstage/core-plugin-api": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", "@backstage/version-bridge": "workspace:^", "lodash": "^4.17.21" }, diff --git a/packages/core-compat-api/report.api.md b/packages/core-compat-api/report.api.md index 50aec1f646..125533c6d3 100644 --- a/packages/core-compat-api/report.api.md +++ b/packages/core-compat-api/report.api.md @@ -14,7 +14,6 @@ import { AppTheme } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { ComponentType } from 'react'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; -import { ExtensionOverrides } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { ExternalRouteRef as ExternalRouteRef_2 } from '@backstage/frontend-plugin-api'; import { FeatureFlag } from '@backstage/core-plugin-api'; @@ -34,7 +33,13 @@ export function compatWrapper(element: ReactNode): React_2.JSX.Element; // @public (undocumented) export function convertLegacyApp( rootElement: React_2.JSX.Element, -): (FrontendPlugin | FrontendModule | ExtensionOverrides)[]; + options?: ConvertLegacyAppOptions, +): (FrontendPlugin | FrontendModule)[]; + +// @public (undocumented) +export interface ConvertLegacyAppOptions { + entityPage?: React_2.JSX.Element; +} // @public (undocumented) export function convertLegacyAppOptions(options?: { diff --git a/packages/core-compat-api/src/collectEntityPageContents.test.tsx b/packages/core-compat-api/src/collectEntityPageContents.test.tsx new file mode 100644 index 0000000000..53aa90f247 --- /dev/null +++ b/packages/core-compat-api/src/collectEntityPageContents.test.tsx @@ -0,0 +1,171 @@ +/* + * 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 { ExtensionAttachToSpec } from '@backstage/frontend-plugin-api'; +import { EntityLayout, EntitySwitch, isKind } from '@backstage/plugin-catalog'; +import React from 'react'; +import { collectEntityPageContents } from './collectEntityPageContents'; +import { + createComponentExtension, + createPlugin, +} from '@backstage/core-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { + resolveExtensionDefinition, + toInternalExtension, +} from '../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; + +const fooPlugin = createPlugin({ + id: 'foo', +}); + +const FooContent = fooPlugin.provide( + createComponentExtension({ + name: 'FooContent', + component: { sync: () =>

foo content
}, + }), +); +const OtherFooContent = fooPlugin.provide( + createComponentExtension({ + name: 'OtherFooContent', + component: { sync: () =>
other foo content
}, + }), +); + +const simpleTestContent = ( + + +
overview content
+
+ + + + +
bar content
+
+
+); + +const otherTestContent = ( + + +
other overview content
+
+ + + +
+); + +function collect(element: React.JSX.Element) { + const result = new Array<{ + id: string; + attachTo: ExtensionAttachToSpec; + }>(); + + collectEntityPageContents(element, { + discoverExtension(extension, plugin) { + const ext = toInternalExtension( + resolveExtensionDefinition(extension, { + namespace: plugin?.getId() ?? 'test', + }), + ); + result.push({ id: ext.id, attachTo: ext.attachTo }); + }, + }); + return result; +} + +describe('collectEntityPageContents', () => { + it('should collect contents from a simple entity page', () => { + expect(collect(simpleTestContent)).toMatchInlineSnapshot(` + [ + { + "attachTo": { + "id": "entity-content:catalog/overview", + "input": "cards", + }, + "id": "entity-card:test/discovered-1", + }, + { + "attachTo": { + "id": "page:catalog/entity", + "input": "contents", + }, + "id": "entity-content:foo/discovered-1", + }, + { + "attachTo": { + "id": "page:catalog/entity", + "input": "contents", + }, + "id": "entity-content:test/discovered-2", + }, + ] + `); + }); + + it('should collect contents from an entity page with an entity switch', () => { + expect( + collect( + + + {simpleTestContent} + + {otherTestContent} + , + ), + ).toMatchInlineSnapshot(` + [ + { + "attachTo": { + "id": "entity-content:catalog/overview", + "input": "cards", + }, + "id": "entity-card:test/discovered-1", + }, + { + "attachTo": { + "id": "page:catalog/entity", + "input": "contents", + }, + "id": "entity-content:foo/discovered-1", + }, + { + "attachTo": { + "id": "page:catalog/entity", + "input": "contents", + }, + "id": "entity-content:test/discovered-2", + }, + { + "attachTo": { + "id": "entity-content:catalog/overview", + "input": "cards", + }, + "id": "entity-card:test/discovered-2", + }, + { + "attachTo": { + "id": "page:catalog/entity", + "input": "contents", + }, + "id": "entity-content:foo/discovered-3", + }, + ] + `); + }); +}); diff --git a/packages/core-compat-api/src/collectEntityPageContents.ts b/packages/core-compat-api/src/collectEntityPageContents.ts new file mode 100644 index 0000000000..503642733e --- /dev/null +++ b/packages/core-compat-api/src/collectEntityPageContents.ts @@ -0,0 +1,249 @@ +/* + * 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 { + ApiHolder, + getComponentData, + BackstagePlugin as LegacyBackstagePlugin, +} from '@backstage/core-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import React from 'react'; +import { + EntityCardBlueprint, + EntityContentBlueprint, +} from '@backstage/plugin-catalog-react/alpha'; +import { normalizeRoutePath } from './normalizeRoutePath'; + +const ENTITY_SWITCH_KEY = 'core.backstage.entitySwitch'; +const ENTITY_ROUTE_KEY = 'plugin.catalog.entityLayoutRoute'; + +// Placeholder to make sure internal types here are consitent +type Entity = { apiVersion: string; kind: string }; + +type EntityFilter = (entity: Entity, ctx: { apis: ApiHolder }) => boolean; +type AsyncEntityFilter = ( + entity: Entity, + context: { apis: ApiHolder }, +) => boolean | Promise; + +function allFilters( + ...ifs: (EntityFilter | undefined)[] +): EntityFilter | undefined { + const filtered = ifs.filter(Boolean) as EntityFilter[]; + if (!filtered.length) { + return undefined; + } + if (filtered.length === 1) { + return filtered[0]; + } + return (entity, ctx) => filtered.every(ifFunc => ifFunc(entity, ctx)); +} + +function anyFilters( + ...ifs: (EntityFilter | undefined)[] +): EntityFilter | undefined { + const filtered = ifs.filter(Boolean) as EntityFilter[]; + if (!filtered.length) { + return undefined; + } + if (filtered.length === 1) { + return filtered[0]; + } + return (entity, ctx) => filtered.some(ifFunc => ifFunc(entity, ctx)); +} + +function invertFilter(ifFunc?: EntityFilter): EntityFilter { + if (!ifFunc) { + return () => true; + } + return (entity, ctx) => !ifFunc(entity, ctx); +} + +export function collectEntityPageContents( + entityPageElement: React.JSX.Element, + context: { + discoverExtension( + extension: ExtensionDefinition, + plugin?: LegacyBackstagePlugin, + ): void; + }, +) { + let cardCounter = 1; + let routeCounter = 1; + + function traverse(element: React.ReactNode, parentFilter?: EntityFilter) { + if (!React.isValidElement(element)) { + return; + } + + const pageNode = maybeParseEntityPageNode(element); + if (pageNode) { + if (pageNode.type === 'route') { + const mergedIf = allFilters(parentFilter, pageNode.if); + + if (pageNode.path === '/') { + context.discoverExtension( + EntityCardBlueprint.makeWithOverrides({ + name: `discovered-${cardCounter++}`, + factory(originalFactory, { apis }) { + return originalFactory({ + type: 'content', + filter: mergedIf && (entity => mergedIf(entity, { apis })), + loader: () => Promise.resolve(pageNode.children), + }); + }, + }), + ); + } else { + const name = `discovered-${routeCounter++}`; + + context.discoverExtension( + EntityContentBlueprint.makeWithOverrides({ + name, + factory(originalFactory, { apis }) { + return originalFactory({ + defaultPath: normalizeRoutePath(pageNode.path), + defaultTitle: pageNode.title, + filter: mergedIf && (entity => mergedIf(entity, { apis })), + loader: () => Promise.resolve(pageNode.children), + }); + }, + }), + getComponentData( + pageNode.children, + 'core.plugin', + ), + ); + } + } + if (pageNode.type === 'switch') { + if (pageNode.renderMultipleMatches === 'all') { + for (const entityCase of pageNode.cases) { + traverse( + entityCase.children, + allFilters(parentFilter, entityCase.if), + ); + } + } else { + let previousIf: EntityFilter = () => false; + for (const entityCase of pageNode.cases) { + const didNotMatchEarlier = invertFilter(previousIf); + traverse( + entityCase.children, + allFilters(parentFilter, entityCase.if, didNotMatchEarlier), + ); + previousIf = anyFilters(previousIf, entityCase.if)!; + } + } + } + return; + } + + React.Children.forEach( + (element.props as { children?: React.ReactNode })?.children, + child => { + traverse(child, parentFilter); + }, + ); + } + + traverse(entityPageElement); +} + +type EntityRoute = { + type: 'route'; + path: string; + title: string; + if?: EntityFilter; + children: JSX.Element; +}; + +type EntitySwitchCase = { + if?: EntityFilter; + children: React.ReactNode; +}; + +type EntitySwitch = { + type: 'switch'; + cases: EntitySwitchCase[]; + renderMultipleMatches: 'first' | 'all'; +}; + +function wrapAsyncEntityFilter( + asyncFilter?: AsyncEntityFilter, +): EntityFilter | undefined { + if (!asyncFilter) { + return asyncFilter; + } + let loggedError = false; + return (entity, ctx) => { + const result = asyncFilter(entity, ctx); + if (result && typeof result === 'object' && 'then' in result) { + if (!loggedError) { + // eslint-disable-next-line no-console + console.error( + `collectEntityPageContents does not support async entity filters, skipping filter ${asyncFilter}`, + ); + loggedError = true; + } + return false; + } + return result; + }; +} + +function maybeParseEntityPageNode( + element: React.JSX.Element, +): EntityRoute | EntitySwitch | undefined { + if (getComponentData(element, ENTITY_ROUTE_KEY)) { + const props = element.props as EntityRoute; + return { + type: 'route', + path: props.path, + title: props.title, + if: props.if, + children: props.children, + }; + } + + const parentProps = element.props as { + children?: React.ReactNode; + renderMultipleMatches?: 'first' | 'all'; + }; + + const children = React.Children.toArray(parentProps?.children); + if (!children.length) { + return undefined; + } + + const cases = []; + for (const child of children) { + if (!getComponentData(child, ENTITY_SWITCH_KEY)) { + return undefined; + } + const props = (child as { props: EntitySwitchCase }).props; + + cases.push({ + if: wrapAsyncEntityFilter(props.if), + children: props.children, + }); + } + return { + type: 'switch', + cases, + renderMultipleMatches: parentProps?.renderMultipleMatches ?? 'first', + }; +} diff --git a/packages/core-compat-api/src/collectLegacyRoutes.test.tsx b/packages/core-compat-api/src/collectLegacyRoutes.test.tsx index ce0b055c00..2d64f88535 100644 --- a/packages/core-compat-api/src/collectLegacyRoutes.test.tsx +++ b/packages/core-compat-api/src/collectLegacyRoutes.test.tsx @@ -57,7 +57,7 @@ describe('collectLegacyRoutes', () => { expect( collected.map(p => ({ - id: p.id, + id: p.$$type === '@backstage/FrontendPlugin' ? p.id : p.pluginId, extensions: OpaqueFrontendPlugin.toInternal(p).extensions.map(e => ({ id: e.id, attachTo: e.attachTo, @@ -177,7 +177,7 @@ describe('collectLegacyRoutes', () => { expect( collected.map(p => ({ - id: p.id, + id: p.$$type === '@backstage/FrontendPlugin' ? p.id : p.pluginId, extensions: OpaqueFrontendPlugin.toInternal(p).extensions.map(e => ({ id: e.id, attachTo: e.attachTo, diff --git a/packages/core-compat-api/src/collectLegacyRoutes.tsx b/packages/core-compat-api/src/collectLegacyRoutes.tsx index b0a55f9ddd..072e6b3bb2 100644 --- a/packages/core-compat-api/src/collectLegacyRoutes.tsx +++ b/packages/core-compat-api/src/collectLegacyRoutes.tsx @@ -30,6 +30,8 @@ import { createFrontendPlugin, ApiBlueprint, PageBlueprint, + FrontendModule, + createFrontendModule, } from '@backstage/frontend-plugin-api'; import React, { Children, ReactNode, isValidElement } from 'react'; import { Route, Routes } from 'react-router-dom'; @@ -38,6 +40,8 @@ import { convertLegacyRouteRefs, } from './convertLegacyRouteRef'; import { compatWrapper } from './compatWrapper'; +import { collectEntityPageContents } from './collectEntityPageContents'; +import { normalizeRoutePath } from './normalizeRoutePath'; /* @@ -102,7 +106,7 @@ function makeRoutingShimExtension(options: { }); } -function visitRouteChildren(options: { +export function visitRouteChildren(options: { children: ReactNode; parentExtensionId: string; context: { @@ -157,7 +161,10 @@ function visitRouteChildren(options: { /** @internal */ export function collectLegacyRoutes( flatRoutesElement: JSX.Element, -): FrontendPlugin[] { + entityPage?: JSX.Element, +): (FrontendPlugin | FrontendModule)[] { + const output = new Array(); + const pluginExtensions = new Map< LegacyBackstagePlugin, ExtensionDefinition[] @@ -230,7 +237,7 @@ export function collectLegacyRoutes( factory(originalFactory, { inputs: _inputs }) { // todo(blam): why do we not use the inputs here? return originalFactory({ - defaultPath: path[0] === '/' ? path.slice(1) : path, + defaultPath: normalizeRoutePath(path), routeRef: routeRef ? convertLegacyRouteRef(routeRef) : undefined, loader: async () => compatWrapper( @@ -262,20 +269,59 @@ export function collectLegacyRoutes( }, ); - return Array.from(pluginExtensions).map(([plugin, extensions]) => - createFrontendPlugin({ - id: plugin.getId(), - extensions: [ - ...extensions, - ...Array.from(plugin.getApis()).map(factory => - ApiBlueprint.make({ - name: factory.api.id, - params: { factory }, - }), - ), - ], - routes: convertLegacyRouteRefs(plugin.routes ?? {}), - externalRoutes: convertLegacyRouteRefs(plugin.externalRoutes ?? {}), - }), - ); + if (entityPage) { + collectEntityPageContents(entityPage, { + discoverExtension(extension, plugin) { + if (!plugin || plugin.getId() === 'catalog') { + getPluginExtensions(orphanRoutesPlugin).push(extension); + } else { + getPluginExtensions(plugin).push(extension); + } + }, + }); + + const extensions = new Array(); + visitRouteChildren({ + children: entityPage, + parentExtensionId: `page:catalog/entity`, + context: { + pluginId: 'catalog', + extensions, + getUniqueName, + discoverPlugin(plugin) { + if (plugin.getId() !== 'catalog') { + getPluginExtensions(plugin); + } + }, + }, + }); + + output.push( + createFrontendModule({ + pluginId: 'catalog', + extensions, + }), + ); + } + + for (const [plugin, extensions] of pluginExtensions) { + output.push( + createFrontendPlugin({ + id: plugin.getId(), + extensions: [ + ...extensions, + ...Array.from(plugin.getApis()).map(factory => + ApiBlueprint.make({ + name: factory.api.id, + params: { factory }, + }), + ), + ], + routes: convertLegacyRouteRefs(plugin.routes ?? {}), + externalRoutes: convertLegacyRouteRefs(plugin.externalRoutes ?? {}), + }), + ); + } + + return output; } diff --git a/packages/core-compat-api/src/convertLegacyApp.test.tsx b/packages/core-compat-api/src/convertLegacyApp.test.tsx index 7153fcb4be..c532c6da1a 100644 --- a/packages/core-compat-api/src/convertLegacyApp.test.tsx +++ b/packages/core-compat-api/src/convertLegacyApp.test.tsx @@ -21,6 +21,16 @@ import { ScoreBoardPage } from '@oriflame/backstage-plugin-score-card'; import React, { ReactNode } from 'react'; import { Route } from 'react-router-dom'; import { convertLegacyApp } from './convertLegacyApp'; +import { + createApiFactory, + createComponentExtension, + createPlugin, +} from '@backstage/core-plugin-api'; +import { EntityLayout, EntitySwitch, isKind } from '@backstage/plugin-catalog'; +import { renderInTestApp } from '@backstage/frontend-test-utils'; +import { default as catalogPlugin } from '@backstage/plugin-catalog/alpha'; +import { catalogApiRef } from '@backstage/plugin-catalog-react'; +import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; const Root = ({ children }: { children: ReactNode }) => <>{children}; @@ -159,4 +169,153 @@ describe('convertLegacyApp', () => { }), ]); }); + + it('should convert entity pages', async () => { + const fooPlugin = createPlugin({ + id: 'foo', + }); + + const FooContent = fooPlugin.provide( + createComponentExtension({ + name: 'FooContent', + component: { sync: () =>
foo content
}, + }), + ); + const OtherFooContent = fooPlugin.provide( + createComponentExtension({ + name: 'OtherFooContent', + component: { sync: () =>
other foo content
}, + }), + ); + + const simpleTestContent = ( + + +
overview content
+
+ + + + +
bar content
+
+
+ ); + + const otherTestContent = ( + + +
other overview content
+
+ + + +
+ ); + + const entityPage = ( + + + {simpleTestContent} + + {otherTestContent} + + ); + + const converted = convertLegacyApp( + + test} /> + , + { entityPage }, + ); + + const catalogOverride = catalogPlugin.withOverrides({ + extensions: [ + catalogPlugin.getExtension('api:catalog').override({ + params: { + factory: createApiFactory( + catalogApiRef, + catalogApiMock({ + entities: [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'test', + metadata: { + name: 'x', + }, + spec: {}, + }, + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'other', + metadata: { + name: 'x', + }, + spec: {}, + }, + ], + }), + ), + }, + }), + ], + }); + + // Overview + const renderOverviewTest = await renderInTestApp(
, { + features: [catalogOverride, ...converted], + initialRouteEntries: ['/catalog/default/test/x'], + }); + await expect( + renderOverviewTest.findByText('overview content'), + ).resolves.toBeInTheDocument(); + renderOverviewTest.unmount(); + + const renderOverviewOther = await renderInTestApp(
, { + features: [catalogOverride, ...converted], + initialRouteEntries: ['/catalog/default/other/x'], + }); + await expect( + renderOverviewOther.findByText('other overview content'), + ).resolves.toBeInTheDocument(); + renderOverviewOther.unmount(); + + // Foo tab + const renderFooTest = await renderInTestApp(
, { + features: [catalogOverride, ...converted], + initialRouteEntries: ['/catalog/default/test/x/foo'], + }); + await expect( + renderFooTest.findByText('foo content'), + ).resolves.toBeInTheDocument(); + renderFooTest.unmount(); + + const renderFooOther = await renderInTestApp(
, { + features: [catalogOverride, ...converted], + initialRouteEntries: ['/catalog/default/other/x/foo'], + }); + await expect( + renderFooOther.findByText('other foo content'), + ).resolves.toBeInTheDocument(); + renderFooOther.unmount(); + + // Bar tab + const renderBarTest = await renderInTestApp(
, { + features: [catalogOverride, ...converted], + initialRouteEntries: ['/catalog/default/test/x/bar'], + }); + await expect( + renderBarTest.findByText('bar content'), + ).resolves.toBeInTheDocument(); + renderBarTest.unmount(); + + const renderBarOther = await renderInTestApp(
, { + features: [catalogOverride, ...converted], + initialRouteEntries: ['/catalog/default/other/x/bar'], + }); + await expect( + renderBarOther.findByText('other overview content'), + ).resolves.toBeInTheDocument(); // /bar does not exist, fall back to rendering overview + renderBarOther.unmount(); + }); }); diff --git a/packages/core-compat-api/src/convertLegacyApp.ts b/packages/core-compat-api/src/convertLegacyApp.ts index f098d090ed..2a62a6c1c6 100644 --- a/packages/core-compat-api/src/convertLegacyApp.ts +++ b/packages/core-compat-api/src/convertLegacyApp.ts @@ -26,7 +26,6 @@ import { FrontendPlugin, coreExtensionData, createExtension, - ExtensionOverrides, createExtensionInput, createFrontendModule, } from '@backstage/frontend-plugin-api'; @@ -60,12 +59,35 @@ function selectChildren( }); } +/** @public */ +export interface ConvertLegacyAppOptions { + /** + * By providing an entity page element here it will be split up and converted + * into individual extensions for the catalog plugin in the new frontend + * system. + * + * In order to use this option the entity page and other catalog extensions + * must be removed from the legacy app, and the catalog plugin for the new + * frontend system must be installed instead. + * + * In order for this conversion to work the entity page must be a plain React + * element tree without any component wrapping anywhere between the root + * element and the `EntityLayout.Route` elements. + * + * When enabling this conversion you are likely to encounter duplicate entity + * page content provided both via the old structure and the new plugins. Any + * duplicate content needs to be removed from the old structure. + */ + entityPage?: React.JSX.Element; +} + /** @public */ export function convertLegacyApp( rootElement: React.JSX.Element, -): (FrontendPlugin | FrontendModule | ExtensionOverrides)[] { + options: ConvertLegacyAppOptions = {}, +): (FrontendPlugin | FrontendModule)[] { if (getComponentData(rootElement, 'core.type') === 'FlatRoutes') { - return collectLegacyRoutes(rootElement); + return collectLegacyRoutes(rootElement, options?.entityPage); } const appRouterEls = selectChildren( @@ -137,7 +159,7 @@ export function convertLegacyApp( disabled: true, }); - const collectedRoutes = collectLegacyRoutes(routesEl); + const collectedRoutes = collectLegacyRoutes(routesEl, options?.entityPage); return [ ...collectedRoutes, diff --git a/packages/core-compat-api/src/index.ts b/packages/core-compat-api/src/index.ts index 6c2df56d92..b1168184f9 100644 --- a/packages/core-compat-api/src/index.ts +++ b/packages/core-compat-api/src/index.ts @@ -17,7 +17,10 @@ export * from './compatWrapper'; export * from './apis'; -export { convertLegacyApp } from './convertLegacyApp'; +export { + convertLegacyApp, + type ConvertLegacyAppOptions, +} from './convertLegacyApp'; export { convertLegacyAppOptions } from './convertLegacyAppOptions'; export { convertLegacyPlugin } from './convertLegacyPlugin'; export { convertLegacyPageExtension } from './convertLegacyPageExtension'; diff --git a/packages/core-compat-api/src/normalizeRoutePath.test.ts b/packages/core-compat-api/src/normalizeRoutePath.test.ts new file mode 100644 index 0000000000..9e76d92167 --- /dev/null +++ b/packages/core-compat-api/src/normalizeRoutePath.test.ts @@ -0,0 +1,36 @@ +/* + * 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 { normalizeRoutePath } from './normalizeRoutePath'; + +describe('normalizeRoutePath', () => { + it('should normalize the path', () => { + expect(normalizeRoutePath('')).toBe('/'); + expect(normalizeRoutePath('/')).toBe('/'); + expect(normalizeRoutePath('/*')).toBe('/'); + expect(normalizeRoutePath('/////')).toBe('/'); + expect(normalizeRoutePath('*//*//*')).toBe('/'); + expect(normalizeRoutePath('/foo')).toBe('/foo'); + expect(normalizeRoutePath('/foo/')).toBe('/foo'); + expect(normalizeRoutePath('/foo/*')).toBe('/foo'); + expect(normalizeRoutePath('/foo/**')).toBe('/foo'); + expect(normalizeRoutePath('/foo//**')).toBe('/foo'); + expect(normalizeRoutePath('/foo/*/*')).toBe('/foo'); + expect(normalizeRoutePath('//foo/*/*')).toBe('/foo'); + expect(normalizeRoutePath('/foo/bar//*/*')).toBe('/foo/bar'); + expect(normalizeRoutePath('/foo//bar//*/*')).toBe('/foo//bar'); + }); +}); diff --git a/packages/core-compat-api/src/normalizeRoutePath.ts b/packages/core-compat-api/src/normalizeRoutePath.ts new file mode 100644 index 0000000000..7d22966362 --- /dev/null +++ b/packages/core-compat-api/src/normalizeRoutePath.ts @@ -0,0 +1,32 @@ +/* + * 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. + */ + +/** + * Normalizes the path to make sure it always starts with a single '/' and do not end with '/' or '*' unless empty + */ +export function normalizeRoutePath(path: string) { + let normalized = path; + while (normalized.endsWith('/') || normalized.endsWith('*')) { + normalized = normalized.slice(0, -1); + } + while (normalized.startsWith('/')) { + normalized = normalized.slice(1); + } + if (!normalized) { + return '/'; + } + return `/${normalized}`; +} diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index 074f1cc922..934b71b8d4 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/core-components +## 0.16.5-next.1 + +### Patch Changes + +- 48aab13: Add i18n support for scaffolder-react plugin +- Updated dependencies + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.4 + - @backstage/version-bridge@1.0.11 + ## 0.16.5-next.0 ### Patch Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 082f3f28c9..847156abee 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-components", - "version": "0.16.5-next.0", + "version": "0.16.5-next.1", "description": "Core components used by Backstage plugins and apps", "backstage": { "role": "web-library" diff --git a/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx b/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx index 8c18a1feee..f65db08393 100644 --- a/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx +++ b/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { ReactNode, useState, useEffect } from 'react'; +import React, { ReactNode, useMemo } from 'react'; import { useApi, storageApiRef } from '@backstage/core-plugin-api'; import useObservable from 'react-use/esm/useObservable'; import classNames from 'classnames'; @@ -102,23 +102,17 @@ export const DismissableBanner = (props: Props) => { const classes = useStyles(); const storageApi = useApi(storageApiRef); const notificationsStore = storageApi.forBucket('notifications'); - const rawDismissedBanners = - notificationsStore.snapshot('dismissedBanners').value ?? []; - - const [dismissedBanners, setDismissedBanners] = useState( - new Set(rawDismissedBanners), - ); - const observedItems = useObservable( notificationsStore.observe$('dismissedBanners'), + notificationsStore.snapshot('dismissedBanners'), ); - useEffect(() => { - if (observedItems?.value) { - const currentValue = observedItems?.value ?? []; - setDismissedBanners(new Set(currentValue)); - } - }, [observedItems?.value]); + const dismissedBanners = useMemo( + () => new Set(observedItems.value ?? []), + [observedItems.value], + ); + + const loadingSettings = observedItems.presence === 'unknown'; const handleClick = () => { notificationsStore.set('dismissedBanners', [...dismissedBanners, id]); @@ -131,7 +125,7 @@ export const DismissableBanner = (props: Props) => { ? { vertical: 'bottom', horizontal: 'center' } : { vertical: 'top', horizontal: 'center' } } - open={!dismissedBanners.has(id)} + open={!loadingSettings && !dismissedBanners.has(id)} classes={{ root: classNames(classes.root, !fixed && classes.topPosition), }} diff --git a/packages/core-components/src/components/Select/Select.test.tsx b/packages/core-components/src/components/Select/Select.test.tsx index b83ac68d5b..505152db5f 100644 --- a/packages/core-components/src/components/Select/Select.test.tsx +++ b/packages/core-components/src/components/Select/Select.test.tsx @@ -93,4 +93,62 @@ describe(', + ); + + // Verify Chip component exist + const chip = getByTestId('chip'); + expect(chip).toBeInTheDocument(); + expect(chip.textContent).toContain('test 1'); + + // Find cancel icon + const cancelIcon = getByTestId('cancel-icon'); + expect(cancelIcon).toBeInTheDocument(); + + // Verify dropdown is initially closed + expect(queryByText('test 2')).not.toBeInTheDocument(); + + // Fire mouseDown on Chip's CancelIcon that tests if onMouseDown={event => event.stopPropagation()} is working properly + fireEvent.mouseDown(cancelIcon); + + // Verify dropdown is closed after mouseDown on Chip's CancelIcon + expect(queryByText('test 2')).not.toBeInTheDocument(); + + // Delete the Chip + fireEvent.click(cancelIcon); + + // Verify dropdown is still closed after the removal of Chip + expect(queryByText('test 2')).not.toBeInTheDocument(); + + // Verify Chip is removed + expect(chip).not.toBeInTheDocument(); + expect(queryByText('test 1')).not.toBeInTheDocument(); + + // Verify we can still open the dropdown with a click on the select + const selectInput = getByTestId('select'); + expect(selectInput.textContent).toBe('All results'); + + // Simulate click on select + fireEvent.mouseDown(within(selectInput).getByRole('button')); + + // Now dropdown should be open + expect(queryByText('test 2')).toBeInTheDocument(); + }); }); diff --git a/packages/core-components/src/components/Select/Select.tsx b/packages/core-components/src/components/Select/Select.tsx index 6b5174820b..7a862678b8 100644 --- a/packages/core-components/src/components/Select/Select.tsx +++ b/packages/core-components/src/components/Select/Select.tsx @@ -28,6 +28,7 @@ import { withStyles, } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; +import CancelIcon from '@material-ui/icons/Cancel'; import React, { useEffect, useState } from 'react'; import ClosedDropdown from './static/ClosedDropdown'; @@ -222,9 +223,16 @@ export function SelectComponent(props: SelectProps) { const item = items.find(el => el.value === selectedValue); return item ? ( event.stopPropagation()} + /> + } onDelete={handleDelete(selectedValue)} className={classes.chip} /> diff --git a/packages/core-components/src/components/SimpleStepper/SimpleStepper.test.tsx b/packages/core-components/src/components/SimpleStepper/SimpleStepper.test.tsx index 73baff8db2..e4093f07d2 100644 --- a/packages/core-components/src/components/SimpleStepper/SimpleStepper.test.tsx +++ b/packages/core-components/src/components/SimpleStepper/SimpleStepper.test.tsx @@ -173,4 +173,32 @@ describe('Stepper', () => { fireEvent.click(getTextInSlide(rendered, 2)('Back') as Node); expect(rendered.getByText('step1')).toBeInTheDocument(); }); + + it('Handles onBack action properly when activeStep is higher than 0', async () => { + const rendered = await renderInTestApp( + + +
step0
+
+ +
step1
+
+ +
step2
+
+
, + ); + + fireEvent.click(getTextInSlide(rendered, 2)('Back') as Node); + expect(rendered.getByText('step1')).toBeInTheDocument(); + + fireEvent.click(getTextInSlide(rendered, 1)('Back') as Node); + expect(rendered.getByText('step0')).toBeInTheDocument(); + + fireEvent.click(getTextInSlide(rendered, 0)('Next') as Node); + expect(rendered.getByText('step1')).toBeInTheDocument(); + + fireEvent.click(getTextInSlide(rendered, 1)('Next') as Node); + expect(rendered.getByText('step2')).toBeInTheDocument(); + }); }); diff --git a/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx b/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx index 4a1a4ab919..b0b6c1afd0 100644 --- a/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx +++ b/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx @@ -32,6 +32,7 @@ type InternalState = { }; const noop = () => {}; + export const VerticalStepperContext = React.createContext({ stepperLength: 0, stepIndex: 0, @@ -50,7 +51,17 @@ export interface StepperProps { export function SimpleStepper(props: PropsWithChildren) { const { children, elevated, onStepChange, activeStep = 0 } = props; const [stepIndex, setStepIndex] = useState(activeStep); - const [stepHistory, setStepHistory] = useState([0]); + /* + Recreates the stepHistory array based on the activeStep + to make sure the handleBack function of the Footer works when activeStep is higher than 0 + */ + const inOrderRecreatedStepHistory = Array.from( + { length: activeStep + 1 }, + (_, i) => i, + ); + const [stepHistory, setStepHistory] = useState( + inOrderRecreatedStepHistory, + ); useEffect(() => { setStepIndex(activeStep); diff --git a/packages/core-plugin-api/src/extensions/extensions.test.tsx b/packages/core-plugin-api/src/extensions/extensions.test.tsx index 3eca133608..25d29fc392 100644 --- a/packages/core-plugin-api/src/extensions/extensions.test.tsx +++ b/packages/core-plugin-api/src/extensions/extensions.test.tsx @@ -15,7 +15,7 @@ */ import { withLogCollector } from '@backstage/test-utils'; -import { render, screen } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; import React from 'react'; import { useAnalyticsContext } from '../analytics/AnalyticsContext'; import { useApp, ErrorBoundaryFallbackProps } from '../app'; @@ -27,6 +27,7 @@ import { createReactExtension, createRoutableExtension, } from './extensions'; +import { ForwardedError } from '@backstage/errors'; jest.mock('../app'); @@ -120,6 +121,48 @@ describe('extensions', () => { expect(errors[0]).toMatchObject({ detail: new Error('Test error') }); }); + it('should handle failed lazy loads', async () => { + const BrokenComponent = plugin.provide( + createComponentExtension({ + name: 'BrokenComponent', + component: { + lazy: async () => { + if (true as boolean) { + throw new Error('Test error'); + } + return () =>
; + }, + }, + }), + ); + + mocked(useApp).mockReturnValue({ + getComponents: () => ({ + Progress: () => null, + ErrorBoundaryFallback: (props: ErrorBoundaryFallbackProps) => ( + <> + Error in {props.plugin?.getId()}: {String(props.error)} + + ), + }), + }); + + const { error: errors } = await withLogCollector(['error'], async () => { + await act(async () => { + render(); + }); + }); + screen.getByText( + 'Error in my-plugin: Error: Failed lazy loading of the BrokenComponent extension, try to reload the page; caused by Error: Test error', + ); + expect(errors[0]).toMatchObject({ + detail: new ForwardedError( + 'Failed lazy loading of the BrokenComponent extension, try to reload the page', + new Error('Test error'), + ), + }); + }); + it('should wrap extended component with analytics context', async () => { const AnalyticsSpyExtension = plugin.provide( createReactExtension({ diff --git a/packages/core-plugin-api/src/extensions/extensions.tsx b/packages/core-plugin-api/src/extensions/extensions.tsx index b94354fa2c..51af857d4b 100644 --- a/packages/core-plugin-api/src/extensions/extensions.tsx +++ b/packages/core-plugin-api/src/extensions/extensions.tsx @@ -22,6 +22,7 @@ import { attachComponentData } from './componentData'; import { Extension, BackstagePlugin } from '../plugin'; import { PluginErrorBoundary } from './PluginErrorBoundary'; import { routableExtensionRenderedEvent } from '../analytics/Tracker'; +import { ForwardedError } from '@backstage/errors'; /** * Lazy or synchronous retrieving of extension components. @@ -79,62 +80,51 @@ export function createRoutableExtension< return createReactExtension({ component: { lazy: () => - component().then( - InnerComponent => { - const RoutableExtensionWrapper: any = (props: any) => { - const analytics = useAnalytics(); + component().then(InnerComponent => { + const RoutableExtensionWrapper: any = (props: any) => { + const analytics = useAnalytics(); - // Validate that the routing is wired up correctly in the App.tsx - try { - useRouteRef(mountPoint); - } catch (error) { - if (typeof error === 'object' && error !== null) { - const { message } = error as { message?: unknown }; - if ( - typeof message === 'string' && - message.startsWith('No path for ') - ) { - throw new Error( - `Routable extension component with mount point ${mountPoint} was not discovered in the app element tree. ` + - 'Routable extension components may not be rendered by other components and must be ' + - 'directly available as an element within the App provider component.', - ); - } + // Validate that the routing is wired up correctly in the App.tsx + try { + useRouteRef(mountPoint); + } catch (error) { + if (typeof error === 'object' && error !== null) { + const { message } = error as { message?: unknown }; + if ( + typeof message === 'string' && + message.startsWith('No path for ') + ) { + throw new Error( + `Routable extension component with mount point ${mountPoint} was not discovered in the app element tree. ` + + 'Routable extension components may not be rendered by other components and must be ' + + 'directly available as an element within the App provider component.', + ); } - throw error; } + throw error; + } - // This event, never exposed to end-users of the analytics API, - // helps inform which extension metadata gets associated with a - // navigation event when the route navigated to is a gathered - // mountpoint. - useEffect(() => { - analytics.captureEvent(routableExtensionRenderedEvent, ''); - }, [analytics]); + // This event, never exposed to end-users of the analytics API, + // helps inform which extension metadata gets associated with a + // navigation event when the route navigated to is a gathered + // mountpoint. + useEffect(() => { + analytics.captureEvent(routableExtensionRenderedEvent, ''); + }, [analytics]); - return ; - }; + return ; + }; - const componentName = - name || - (InnerComponent as { displayName?: string }).displayName || - InnerComponent.name || - 'LazyComponent'; + const componentName = + name || + (InnerComponent as { displayName?: string }).displayName || + InnerComponent.name || + 'LazyComponent'; - RoutableExtensionWrapper.displayName = `RoutableExtension(${componentName})`; + RoutableExtensionWrapper.displayName = `RoutableExtension(${componentName})`; - return RoutableExtensionWrapper as T; - }, - error => { - const RoutableExtensionWrapper: any = (_: any) => { - const app = useApp(); - const { BootErrorPage } = app.getComponents(); - - return ; - }; - return RoutableExtensionWrapper; - }, - ), + return RoutableExtensionWrapper as T; + }), }, data: { 'core.mountPoint': mountPoint, @@ -225,7 +215,16 @@ export function createReactExtension< if ('lazy' in options.component) { const lazyLoader = options.component.lazy; Component = lazy(() => - lazyLoader().then(component => ({ default: component })), + lazyLoader().then( + component => ({ default: component }), + error => { + const ofExtension = name ? ` of the ${name} extension` : ''; + throw new ForwardedError( + `Failed lazy loading${ofExtension}, try to reload the page`, + error, + ); + }, + ), ) as unknown as T; } else { Component = options.component.sync; diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index a4da0a691e..4aca44a2df 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/create-app +## 0.6.0-next.2 + +### Minor Changes + +- 31731b0: Upgraded the TypeScript version in the template to `5.8`. + +### Patch Changes + +- 19e5c3f: Added link to multi-stage Dockerfile documentation as alternative option +- Updated dependencies + - @backstage/cli-common@0.1.15 + ## 0.5.26-next.1 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index dffe59a294..22f095a823 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/create-app", - "version": "0.5.26-next.1", + "version": "0.6.0-next.2", "description": "A CLI that helps you create your own Backstage app", "backstage": { "role": "cli" diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index fe1b461dbb..dc76465444 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/dev-utils +## 1.1.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/app-defaults@1.6.0-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/theme@0.6.4 + ## 1.1.8-next.1 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 41af496310..7d32d022e7 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/dev-utils", - "version": "1.1.8-next.1", + "version": "1.1.8-next.2", "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 49f554762b..72656ea47e 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,14 @@ # e2e-test +## 0.2.26-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.6.0-next.2 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + ## 0.2.26-next.1 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index f0390de9f9..32b34bf482 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,6 +1,6 @@ { "name": "e2e-test", - "version": "0.2.26-next.1", + "version": "0.2.26-next.2", "description": "E2E test for verifying Backstage packages", "backstage": { "role": "cli" diff --git a/packages/frontend-app-api/CHANGELOG.md b/packages/frontend-app-api/CHANGELOG.md index 83af105777..b25e5cd722 100644 --- a/packages/frontend-app-api/CHANGELOG.md +++ b/packages/frontend-app-api/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/frontend-app-api +## 0.11.0-next.2 + +### Minor Changes + +- abcdf44: **BREAKING**: The returned object from `createSpecializedApp` no longer contains a `createRoot()` method, and it instead now contains `apis` and `tree`. + + You can replace existing usage of `app.createRoot()` with the following: + + ```ts + const root = tree.root.instance?.getData(coreExtensionData.reactElement); + ``` + +- 8250ffe: **BREAKING**: Dropped support for the removed opaque `@backstage/ExtensionOverrides` and `@backstage/BackstagePlugin` types. + +### Patch Changes + +- 4d18b55: It's now possible to provide a middleware that wraps all extension factories by passing an `extensionFactoryMiddleware` to either `createApp()` or `createSpecializedApp()`. +- Updated dependencies + - @backstage/frontend-defaults@0.2.0-next.2 + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + ## 0.10.6-next.1 ### Patch Changes diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index 2ee763d7ef..2c280774e4 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.10.6-next.1", + "version": "0.11.0-next.2", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-app-api/report.api.md b/packages/frontend-app-api/report.api.md index d3c2045f85..f24d662df4 100644 --- a/packages/frontend-app-api/report.api.md +++ b/packages/frontend-app-api/report.api.md @@ -8,8 +8,7 @@ import { AppTree } from '@backstage/frontend-plugin-api'; import { ConfigApi } from '@backstage/core-plugin-api'; import { ExtensionFactoryMiddleware } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/frontend-plugin-api'; -import { FrontendModule } from '@backstage/frontend-plugin-api'; -import { FrontendPlugin } from '@backstage/frontend-plugin-api'; +import { FrontendFeature as FrontendFeature_2 } from '@backstage/frontend-plugin-api'; import { RouteRef } from '@backstage/frontend-plugin-api'; import { SubRouteRef } from '@backstage/frontend-plugin-api'; @@ -40,14 +39,6 @@ export function createSpecializedApp(options?: { tree: AppTree; }; -// @public (undocumented) -export type FrontendFeature = - | FrontendPlugin - | FrontendModule - | { - $$type: '@backstage/ExtensionOverrides'; - } - | { - $$type: '@backstage/BackstagePlugin'; - }; +// @public @deprecated (undocumented) +export type FrontendFeature = FrontendFeature_2; ``` diff --git a/packages/frontend-app-api/src/wiring/types.ts b/packages/frontend-app-api/src/wiring/types.ts index 18d9c51d0e..2bf08f327f 100644 --- a/packages/frontend-app-api/src/wiring/types.ts +++ b/packages/frontend-app-api/src/wiring/types.ts @@ -14,16 +14,13 @@ * limitations under the License. */ import { RouteRef } from '@backstage/frontend-plugin-api'; -import { FrontendModule, FrontendPlugin } from '@backstage/frontend-plugin-api'; +import { FrontendFeature as PluginApiFrontendFeature } from '@backstage/frontend-plugin-api'; import { BackstageRouteObject } from '../routing/types'; -/** @public */ -export type FrontendFeature = - | FrontendPlugin - | FrontendModule - // TODO(blam): This is just forwards backwards compatibility, remove after v1.31.0 - | { $$type: '@backstage/ExtensionOverrides' } - | { $$type: '@backstage/BackstagePlugin' }; +/** @public + * @deprecated Use {@link @backstage/frontend-plugin-api#FrontendFeature} instead. + */ +export type FrontendFeature = PluginApiFrontendFeature; /** @internal */ export type RouteInfo = { diff --git a/packages/frontend-defaults/CHANGELOG.md b/packages/frontend-defaults/CHANGELOG.md index 8b385dd639..da18fe1155 100644 --- a/packages/frontend-defaults/CHANGELOG.md +++ b/packages/frontend-defaults/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/frontend-defaults +## 0.2.0-next.2 + +### Minor Changes + +- 8250ffe: **BREAKING**: Dropped support for the removed opaque `@backstage/ExtensionOverrides` and `@backstage/BackstagePlugin` types. + +### Patch Changes + +- 4d18b55: It's now possible to provide a middleware that wraps all extension factories by passing an `extensionFactoryMiddleware` to either `createApp()` or `createSpecializedApp()`. +- abcdf44: Internal refactor to match updated `createSpecializedApp`. +- e3f19db: Feature discovery and resolution logic used in `createApp` is now exposed via the `discoverAvailableFeatures` and `resolveAsyncFeatures` functions respectively. +- Updated dependencies + - @backstage/frontend-app-api@0.11.0-next.2 + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-app@0.1.7-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.1.7-next.1 ### Patch Changes diff --git a/packages/frontend-defaults/package.json b/packages/frontend-defaults/package.json index 80b241cc47..b04c655746 100644 --- a/packages/frontend-defaults/package.json +++ b/packages/frontend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-defaults", - "version": "0.1.7-next.1", + "version": "0.2.0-next.2", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-defaults/report.api.md b/packages/frontend-defaults/report.api.md index ac115566b8..8bfe2ee017 100644 --- a/packages/frontend-defaults/report.api.md +++ b/packages/frontend-defaults/report.api.md @@ -7,7 +7,8 @@ import { Config } from '@backstage/config'; import { ConfigApi } from '@backstage/frontend-plugin-api'; import { CreateAppRouteBinder } from '@backstage/frontend-app-api'; import { ExtensionFactoryMiddleware } from '@backstage/frontend-plugin-api'; -import { FrontendFeature } from '@backstage/frontend-app-api'; +import { FrontendFeature } from '@backstage/frontend-plugin-api'; +import { FrontendFeatureLoader } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; @@ -17,7 +18,7 @@ export function createApp(options?: CreateAppOptions): { createRoot(): JSX_2.Element; }; -// @public +// @public @deprecated export interface CreateAppFeatureLoader { getLoaderName(): string; load(options: { config: ConfigApi }): Promise<{ @@ -38,7 +39,11 @@ export interface CreateAppOptions { | ExtensionFactoryMiddleware | ExtensionFactoryMiddleware[]; // (undocumented) - features?: (FrontendFeature | CreateAppFeatureLoader)[]; + features?: ( + | FrontendFeature + | FrontendFeatureLoader + | CreateAppFeatureLoader + )[]; loadingComponent?: ReactNode; } @@ -49,13 +54,17 @@ export function createPublicSignInApp(options?: CreateAppOptions): { // @public (undocumented) export function discoverAvailableFeatures(config: Config): { - features: FrontendFeature[]; + features: (FrontendFeature | FrontendFeatureLoader)[]; }; // @public (undocumented) export function resolveAsyncFeatures(options: { config: Config; - features?: (FrontendFeature | CreateAppFeatureLoader)[]; + features?: ( + | FrontendFeature + | FrontendFeatureLoader + | CreateAppFeatureLoader + )[]; }): Promise<{ features: FrontendFeature[]; }>; diff --git a/packages/frontend-defaults/src/createApp.tsx b/packages/frontend-defaults/src/createApp.tsx index 69278d33ed..c6ed4759fa 100644 --- a/packages/frontend-defaults/src/createApp.tsx +++ b/packages/frontend-defaults/src/createApp.tsx @@ -19,6 +19,8 @@ import { ConfigApi, coreExtensionData, ExtensionFactoryMiddleware, + FrontendFeature, + FrontendFeatureLoader, } from '@backstage/frontend-plugin-api'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { defaultConfigLoaderSync } from '../../core-app-api/src/app/defaultConfigLoader'; @@ -27,7 +29,6 @@ import { overrideBaseUrlConfigs } from '../../core-app-api/src/app/overrideBaseU import { ConfigReader } from '@backstage/config'; import { CreateAppRouteBinder, - FrontendFeature, createSpecializedApp, } from '@backstage/frontend-app-api'; import appPlugin from '@backstage/plugin-app'; @@ -38,6 +39,7 @@ import { resolveAsyncFeatures } from './resolution'; * A source of dynamically loaded frontend features. * * @public + * @deprecated Use the {@link @backstage/frontend-plugin-api#createFrontendFeatureLoader} function instead. */ export interface CreateAppFeatureLoader { /** @@ -59,7 +61,11 @@ export interface CreateAppFeatureLoader { * @public */ export interface CreateAppOptions { - features?: (FrontendFeature | CreateAppFeatureLoader)[]; + features?: ( + | FrontendFeature + | FrontendFeatureLoader + | CreateAppFeatureLoader + )[]; configLoader?: () => Promise<{ config: ConfigApi }>; bindRoutes?(context: { bind: CreateAppRouteBinder }): void; /** @@ -94,15 +100,16 @@ export function createApp(options?: CreateAppOptions): { overrideBaseUrlConfigs(defaultConfigLoaderSync()), ); - const { features: discoveredFeatures } = discoverAvailableFeatures(config); - const { features: providedFeatures } = await resolveAsyncFeatures({ + const { features: discoveredFeaturesAndLoaders } = + discoverAvailableFeatures(config); + const { features: loadedFeatures } = await resolveAsyncFeatures({ config, - features: options?.features, + features: [...discoveredFeaturesAndLoaders, ...(options?.features ?? [])], }); const app = createSpecializedApp({ config, - features: [appPlugin, ...discoveredFeatures, ...providedFeatures], + features: [appPlugin, ...loadedFeatures], bindRoutes: options?.bindRoutes, extensionFactoryMiddleware: options?.extensionFactoryMiddleware, }); diff --git a/packages/frontend-defaults/src/discovery.test.ts b/packages/frontend-defaults/src/discovery.test.ts index 80be0b25c5..52a2fcde11 100644 --- a/packages/frontend-defaults/src/discovery.test.ts +++ b/packages/frontend-defaults/src/discovery.test.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -import { createFrontendPlugin } from '@backstage/frontend-plugin-api'; +import { + createFrontendFeatureLoader, + createFrontendPlugin, +} from '@backstage/frontend-plugin-api'; import { discoverAvailableFeatures } from './discovery'; import { ConfigReader } from '@backstage/config'; @@ -51,6 +54,20 @@ describe('discoverAvailableFeatures', () => { }); }); + it('should discover a frontend feature loader', () => { + const testLoader = createFrontendFeatureLoader({ + loader() { + return []; + }, + }); + globalSpy.mockReturnValue({ + modules: [{ default: testLoader }], + }); + expect(discoverAvailableFeatures(config)).toEqual({ + features: [testLoader], + }); + }); + it('should ignore garbage', () => { globalSpy.mockReturnValueOnce({ modules: [{ default: null }] }); expect(discoverAvailableFeatures(config)).toEqual({ features: [] }); diff --git a/packages/frontend-defaults/src/discovery.ts b/packages/frontend-defaults/src/discovery.ts index e82f2acfda..6da6146389 100644 --- a/packages/frontend-defaults/src/discovery.ts +++ b/packages/frontend-defaults/src/discovery.ts @@ -15,7 +15,11 @@ */ import { Config, ConfigReader } from '@backstage/config'; -import { FrontendFeature } from '@backstage/frontend-app-api'; +import { + FrontendFeature, + FrontendFeatureLoader, +} from '@backstage/frontend-plugin-api'; +import { isBackstageFeatureLoader } from './resolution'; interface DiscoveryGlobal { modules: Array<{ name: string; export?: string; default: unknown }>; @@ -56,7 +60,7 @@ function readPackageDetectionConfig(config: Config) { * @public */ export function discoverAvailableFeatures(config: Config): { - features: FrontendFeature[]; + features: (FrontendFeature | FrontendFeatureLoader)[]; } { const discovered = ( window as { '__@backstage/discovered__'?: DiscoveryGlobal } @@ -80,7 +84,7 @@ export function discoverAvailableFeatures(config: Config): { return true; }) .map(m => m.default) - .filter(isBackstageFeature) ?? [], + .filter(isFeatureOrLoader) ?? [], }; } @@ -88,12 +92,14 @@ function isBackstageFeature(obj: unknown): obj is FrontendFeature { if (obj !== null && typeof obj === 'object' && '$$type' in obj) { return ( obj.$$type === '@backstage/FrontendPlugin' || - obj.$$type === '@backstage/FrontendModule' || - // TODO: Remove this once the old plugin type and extension overrides - // are no longer supported - obj.$$type === '@backstage/BackstagePlugin' || - obj.$$type === '@backstage/ExtensionOverrides' + obj.$$type === '@backstage/FrontendModule' ); } return false; } + +function isFeatureOrLoader( + obj: unknown, +): obj is FrontendFeature | FrontendFeatureLoader { + return isBackstageFeature(obj) || isBackstageFeatureLoader(obj); +} diff --git a/packages/frontend-defaults/src/resolution.test.ts b/packages/frontend-defaults/src/resolution.test.ts index d833716666..609b975a33 100644 --- a/packages/frontend-defaults/src/resolution.test.ts +++ b/packages/frontend-defaults/src/resolution.test.ts @@ -15,7 +15,9 @@ */ import { + createFrontendFeatureLoader, createFrontendPlugin, + FrontendFeatureLoader, PageBlueprint, } from '@backstage/frontend-plugin-api'; import { CreateAppFeatureLoader } from './createApp'; @@ -69,7 +71,7 @@ describe('resolveAsyncFeatures', () => { ]); }); - it('supports feature loaders', async () => { + it('supports deprecated feature loaders', async () => { const loader: CreateAppFeatureLoader = { getLoaderName() { return 'test-loader'; @@ -118,7 +120,7 @@ describe('resolveAsyncFeatures', () => { ]); }); - it('should propagate errors thrown by feature loaders', async () => { + it('should propagate errors thrown by deprecated feature loaders', async () => { const loader: CreateAppFeatureLoader = { getLoaderName() { return 'test-loader'; @@ -137,4 +139,65 @@ describe('resolveAsyncFeatures', () => { `"Failed to read frontend features from loader 'test-loader', TypeError: boom"`, ); }); + + it('supports feature loaders', async () => { + const loader: FrontendFeatureLoader = createFrontendFeatureLoader({ + async loader({ config: _ }) { + return [ + createFrontendPlugin({ + id: 'test', + extensions: [ + PageBlueprint.make({ + params: { + defaultPath: '/', + loader: () => new Promise(() => {}), + }, + }), + ], + }), + ]; + }, + }); + + const { features } = await resolveAsyncFeatures({ + config: mockApis.config(), + features: [loader], + }); + + expect(features).toMatchObject([ + { + $$type: '@backstage/FrontendPlugin', + id: 'test', + version: 'v1', + extensions: [ + { + $$type: '@backstage/Extension', + id: 'page:test', + version: 'v2', + attachTo: { + id: 'app/routes', + input: 'routes', + }, + }, + ], + }, + ]); + }); + + it('should propagate errors thrown by feature loaders', async () => { + const loader: FrontendFeatureLoader = createFrontendFeatureLoader({ + async loader({ config: _ }) { + throw new TypeError('boom'); + }, + }); + + await expect(() => + resolveAsyncFeatures({ + config: mockApis.config(), + features: [loader], + }), + ).rejects.toThrow( + /^Failed to read frontend features from loader created at .*: TypeError: boom$/, + ); + }); }); diff --git a/packages/frontend-defaults/src/resolution.ts b/packages/frontend-defaults/src/resolution.ts index e51ecbc2d3..6ab26c3d1d 100644 --- a/packages/frontend-defaults/src/resolution.ts +++ b/packages/frontend-defaults/src/resolution.ts @@ -16,30 +16,98 @@ import { Config } from '@backstage/config'; import { stringifyError } from '@backstage/errors'; -import { FrontendFeature } from '@backstage/frontend-app-api'; +import { + FrontendFeature, + FrontendFeatureLoader, +} from '@backstage/frontend-plugin-api'; import { CreateAppFeatureLoader } from './createApp'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { isInternalFrontendFeatureLoader } from '../../frontend-plugin-api/src/wiring/createFrontendFeatureLoader'; /** @public */ export async function resolveAsyncFeatures(options: { config: Config; - features?: (FrontendFeature | CreateAppFeatureLoader)[]; + features?: ( + | FrontendFeature + | FrontendFeatureLoader + | CreateAppFeatureLoader + )[]; }): Promise<{ features: FrontendFeature[] }> { - const features = []; - for (const entry of options.features ?? []) { - if ('load' in entry) { + const features: (FrontendFeature | FrontendFeatureLoader)[] = []; + + // Separate deprecated CreateAppFeatureLoader elements from the frontend features, + // and manage the deprecated elements first. + for (const item of options?.features ?? []) { + if ('load' in item) { try { - const result = await entry.load({ config: options.config }); + const result = await item.load({ config: options.config }); features.push(...result.features); } catch (e) { throw new Error( - `Failed to read frontend features from loader '${entry.getLoaderName()}', ${stringifyError( + `Failed to read frontend features from loader '${item.getLoaderName()}', ${stringifyError( e, )}`, ); } } else { - features.push(entry); + features.push(item); } } - return { features }; + + const loadedFeatures: FrontendFeature[] = []; + const alreadyMetFeatureLoaders: FrontendFeatureLoader[] = []; + const maxRecursionDepth = 5; + + async function applyFeatureLoaders( + featuresOrLoaders: (FrontendFeature | FrontendFeatureLoader)[], + recursionDepth: number, + ) { + if (featuresOrLoaders.length === 0) { + return; + } + + for (const featureOrLoader of featuresOrLoaders) { + if (isBackstageFeatureLoader(featureOrLoader)) { + if (alreadyMetFeatureLoaders.some(l => l === featureOrLoader)) { + continue; + } + if (isInternalFrontendFeatureLoader(featureOrLoader)) { + if (recursionDepth > maxRecursionDepth) { + throw new Error( + `Maximum feature loading recursion depth (${maxRecursionDepth}) reached for the feature loader ${featureOrLoader.description}`, + ); + } + alreadyMetFeatureLoaders.push(featureOrLoader); + let result: (FrontendFeature | FrontendFeatureLoader)[]; + try { + result = await featureOrLoader.loader({ config: options.config }); + } catch (e) { + throw new Error( + `Failed to read frontend features from loader ${ + featureOrLoader.description + }: ${stringifyError(e)}`, + ); + } + await applyFeatureLoaders(result, recursionDepth + 1); + } + } else { + loadedFeatures.push(featureOrLoader); + } + } + } + + await applyFeatureLoaders(features, 1); + + return { features: loadedFeatures }; +} + +export function isBackstageFeatureLoader( + obj: unknown, +): obj is FrontendFeatureLoader { + return ( + obj !== null && + typeof obj === 'object' && + '$$type' in obj && + obj.$$type === '@backstage/FrontendFeatureLoader' + ); } diff --git a/packages/frontend-internal/CHANGELOG.md b/packages/frontend-internal/CHANGELOG.md index 1a737ee40f..04ec065aab 100644 --- a/packages/frontend-internal/CHANGELOG.md +++ b/packages/frontend-internal/CHANGELOG.md @@ -1,5 +1,14 @@ # @internal/frontend +## 0.0.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + ## 0.0.7-next.1 ### Patch Changes diff --git a/packages/frontend-internal/package.json b/packages/frontend-internal/package.json index b5524a6cc2..3aa48e03d0 100644 --- a/packages/frontend-internal/package.json +++ b/packages/frontend-internal/package.json @@ -1,6 +1,6 @@ { "name": "@internal/frontend", - "version": "0.0.7-next.1", + "version": "0.0.7-next.2", "backstage": { "role": "web-library", "inline": true diff --git a/packages/frontend-plugin-api/CHANGELOG.md b/packages/frontend-plugin-api/CHANGELOG.md index 95cb6c846b..48e7368cba 100644 --- a/packages/frontend-plugin-api/CHANGELOG.md +++ b/packages/frontend-plugin-api/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/frontend-plugin-api +## 0.10.0-next.2 + +### Minor Changes + +- 8250ffe: **BREAKING**: Removed the deprecated `ExtensionOverrides` and `FrontendFeature` types. +- 0d1a397: **BREAKING**: Removed deprecated variant of `createExtensionDataRef` where the ID is passed directly. + +### Patch Changes + +- 5aa7f2c: Added a new Utility API, `DialogApi`, which can be used to show dialogs in the React tree that can collect input from the user. +- e23f5e0: Added new `ExtensionMiddlewareFactory` type. +- a6cb67d: The extensions map for plugins created with `createFrontendPlugin` is now sorted alphabetically by ID in the TypeScript type. +- Updated dependencies + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + ## 0.9.6-next.1 ### Patch Changes diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index 3551ac5e75..1e2818eb6e 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.9.6-next.1", + "version": "0.10.0-next.2", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-plugin-api/report.api.md b/packages/frontend-plugin-api/report.api.md index 52d0276952..a8b5937af1 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -610,11 +610,6 @@ export type CreateExtensionBlueprintOptions< dataRefs?: TDataRefs; } & VerifyExtensionFactoryOutput; -// @public @deprecated (undocumented) -export function createExtensionDataRef( - id: string, -): ConfigurableExtensionDataRef; - // @public (undocumented) export function createExtensionDataRef(): { with(options: { @@ -712,6 +707,40 @@ export function createExternalRouteRef< } >; +// @public (undocumented) +export function createFrontendFeatureLoader( + options: CreateFrontendFeatureLoaderOptions, +): FrontendFeatureLoader; + +// @public (undocumented) +export interface CreateFrontendFeatureLoaderOptions { + // (undocumented) + loader(deps: { config: ConfigApi }): + | Iterable< + | FrontendFeature + | FrontendFeatureLoader + | Promise<{ + default: FrontendFeature | FrontendFeatureLoader; + }> + > + | Promise< + Iterable< + | FrontendFeature + | FrontendFeatureLoader + | Promise<{ + default: FrontendFeature | FrontendFeatureLoader; + }> + > + > + | AsyncIterable< + | FrontendFeature + | FrontendFeatureLoader + | { + default: FrontendFeature | FrontendFeatureLoader; + } + >; +} + // @public (undocumented) export function createFrontendModule< TId extends string, @@ -1212,12 +1241,6 @@ export interface ExtensionInput< }>; } -// @public (undocumented) -export interface ExtensionOverrides { - // (undocumented) - readonly $$type: '@backstage/ExtensionOverrides'; -} - // @public export interface ExternalRouteRef< TParams extends AnyRouteRefParams = AnyRouteRefParams, @@ -1247,8 +1270,14 @@ export { FetchApi }; export { fetchApiRef }; -// @public @deprecated (undocumented) -export type FrontendFeature = FrontendPlugin | ExtensionOverrides; +// @public (undocumented) +export type FrontendFeature = FrontendPlugin | FrontendModule; + +// @public (undocumented) +export interface FrontendFeatureLoader { + // (undocumented) + readonly $$type: '@backstage/FrontendFeatureLoader'; +} // @public (undocumented) export interface FrontendModule { diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionDataRef.test.ts b/packages/frontend-plugin-api/src/wiring/createExtensionDataRef.test.ts index 4b652298f1..b5acf78384 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionDataRef.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionDataRef.test.ts @@ -29,15 +29,6 @@ describe('createExtensionDataRef', () => { expect(String(refOptional)).toBe('ExtensionDataRef{id=foo,optional=true}'); }); - it('can be created and read in the deprecated way', () => { - const ref = createExtensionDataRef('foo'); - expect(ref.id).toBe('foo'); - expect(String(ref)).toBe('ExtensionDataRef{id=foo,optional=false}'); - const refOptional = ref.optional(); - expect(refOptional.id).toBe('foo'); - expect(String(refOptional)).toBe('ExtensionDataRef{id=foo,optional=true}'); - }); - it('can be used to encapsulate a value', () => { const ref = createExtensionDataRef().with({ id: 'foo' }); const val: ExtensionDataValue = ref('hello'); diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionDataRef.ts b/packages/frontend-plugin-api/src/wiring/createExtensionDataRef.ts index 80413c1688..94acb192a8 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionDataRef.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionDataRef.ts @@ -60,26 +60,12 @@ export interface ConfigurableExtensionDataRef< (t: TData): ExtensionDataValue; } -/** - * @public - * @deprecated Use the following form instead: `createExtensionDataRef().with({ id: 'core.foo' })` - */ -export function createExtensionDataRef( - id: string, -): ConfigurableExtensionDataRef; /** @public */ export function createExtensionDataRef(): { with(options: { id: TId; }): ConfigurableExtensionDataRef; -}; -export function createExtensionDataRef(id?: string): - | ConfigurableExtensionDataRef - | { - with(options: { - id: TId; - }): ConfigurableExtensionDataRef; - } { +} { const createRef = (refId: TId) => Object.assign( (value: TData): ExtensionDataValue => ({ @@ -103,9 +89,6 @@ export function createExtensionDataRef(id?: string): }, } as ConfigurableExtensionDataRef, ); - if (id) { - return createRef(id); - } return { with(options: { id: TId }) { return createRef(options.id); diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendFeatureLoader.test.ts b/packages/frontend-plugin-api/src/wiring/createFrontendFeatureLoader.test.ts new file mode 100644 index 0000000000..f240b4f678 --- /dev/null +++ b/packages/frontend-plugin-api/src/wiring/createFrontendFeatureLoader.test.ts @@ -0,0 +1,484 @@ +/* + * 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 React from 'react'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { createApp } from '../../../frontend-defaults/src/createApp'; +import { screen } from '@testing-library/react'; +import { createFrontendPlugin } from './createFrontendPlugin'; +import { JsonObject } from '@backstage/types'; +import { createExtension } from './createExtension'; +import { createExtensionDataRef } from './createExtensionDataRef'; +import { coreExtensionData } from './coreExtensionData'; +import { mockApis, renderWithEffects } from '@backstage/test-utils'; +import { createExtensionInput } from './createExtensionInput'; +import { + CreateFrontendFeatureLoaderOptions, + InternalFrontendFeatureLoader, + createFrontendFeatureLoader, + FrontendFeatureLoader, +} from './createFrontendFeatureLoader'; +import { FrontendFeature } from './types'; + +const nameExtensionDataRef = createExtensionDataRef().with({ + id: 'name', +}); + +function createTestAppRoot({ + features, + config = {}, +}: { + features: (FrontendFeature | FrontendFeatureLoader)[]; + config: JsonObject; +}) { + return createApp({ + features: [...features], + configLoader: async () => ({ config: mockApis.config({ data: config }) }), + }).createRoot(); +} + +describe('createFrontendFeatureLoader', () => { + it('should create several plugins with only one feature loader', async () => { + const featureLoader: FrontendFeatureLoader = createFrontendFeatureLoader({ + loader({ config }) { + const pluginIdPrefix = config.getOptionalString('pluginIdPrefix'); + const extensionNamePrefix = config.getOptionalString( + 'extensionNamePrefix', + ); + return [ + createFrontendPlugin({ + id: `${pluginIdPrefix}-1`, + extensions: [ + createExtension({ + name: '1', + attachTo: { + id: `${pluginIdPrefix}-output/output`, + input: 'names', + }, + output: [nameExtensionDataRef], + factory() { + return [nameExtensionDataRef(`${extensionNamePrefix}-1`)]; + }, + }), + ], + }) as FrontendFeature | FrontendFeatureLoader, + createFrontendPlugin({ + id: `${pluginIdPrefix}-2`, + extensions: [ + createExtension({ + name: '2', + attachTo: { + id: `${pluginIdPrefix}-output/output`, + input: 'names', + }, + output: [nameExtensionDataRef], + factory() { + return [nameExtensionDataRef(`${extensionNamePrefix}-2`)]; + }, + }), + ], + }) as FrontendFeature | FrontendFeatureLoader, + createFrontendPlugin({ + id: `${pluginIdPrefix}-output`, + extensions: [ + createExtension({ + name: 'output', + attachTo: { id: 'app', input: 'root' }, + inputs: { + names: createExtensionInput([nameExtensionDataRef]), + }, + output: [coreExtensionData.reactElement], + factory({ inputs }) { + return [ + coreExtensionData.reactElement( + React.createElement('span', {}, [ + `Names: ${inputs.names + .map(n => n.get(nameExtensionDataRef)) + .join(', ')}`, + ]), + ), + ]; + }, + }), + ], + }) as FrontendFeature | FrontendFeatureLoader, + ]; + }, + } as CreateFrontendFeatureLoaderOptions); + + expect(featureLoader).toBeDefined(); + expect(String(featureLoader)).toMatch( + /^FeatureLoader{description=created at '.*\/packages\/frontend-plugin-api\/src\/wiring\/createFrontendFeatureLoader\.test\.ts:.*'}$/, + ); + + await renderWithEffects( + createTestAppRoot({ + features: [featureLoader], + config: { + app: { extensions: [{ 'app/root': false }] }, + extensionNamePrefix: 'extension', + pluginIdPrefix: 'plugin', + }, + }), + ); + + await expect( + screen.findByText('Names: extension-1, extension-2'), + ).resolves.toBeInTheDocument(); + }); + + it('should propagate errors thrown by feature loaders', async () => { + const featureLoader: FrontendFeature | FrontendFeatureLoader = + createFrontendFeatureLoader({ + async loader(_) { + throw new TypeError('boom'); + }, + }); + + await expect( + renderWithEffects( + createTestAppRoot({ + features: [featureLoader], + config: {}, + }), + ), + ).rejects.toThrow( + /^Failed to read frontend features from loader created at '.*\/packages\/frontend-plugin-api\/src\/wiring\/createFrontendFeatureLoader\.test\.ts:.*': TypeError: boom$/, + ); + }); + + it('should support loading feature loaders', async () => { + const featureLoader: FrontendFeature | FrontendFeatureLoader = + createFrontendFeatureLoader({ + async loader(_) { + return [ + createFrontendPlugin({ + id: 'plugin-0', + extensions: [ + createExtension({ + name: '0', + attachTo: { + id: 'plugin-output/output', + input: 'names', + }, + output: [nameExtensionDataRef], + factory() { + return [nameExtensionDataRef('extension-0')]; + }, + }), + ], + }), + createFrontendFeatureLoader({ + async *loader(__) { + yield createFrontendPlugin({ + id: 'plugin-1', + extensions: [ + createExtension({ + name: '1', + attachTo: { + id: 'plugin-output/output', + input: 'names', + }, + output: [nameExtensionDataRef], + factory() { + return [nameExtensionDataRef('extension-1')]; + }, + }), + ], + }); + yield createFrontendFeatureLoader({ + loader: async ___ => [ + createFrontendPlugin({ + id: 'plugin-2', + extensions: [ + createExtension({ + name: '2', + attachTo: { + id: 'plugin-output/output', + input: 'names', + }, + output: [nameExtensionDataRef], + factory() { + return [nameExtensionDataRef('extension-2')]; + }, + }), + ], + }), + ], + }); + }, + }), + createFrontendPlugin({ + id: 'plugin-output', + extensions: [ + createExtension({ + name: 'output', + attachTo: { id: 'app', input: 'root' }, + inputs: { + names: createExtensionInput([nameExtensionDataRef]), + }, + output: [coreExtensionData.reactElement], + factory({ inputs }) { + return [ + coreExtensionData.reactElement( + React.createElement('span', {}, [ + `Names: ${inputs.names + .map(n => n.get(nameExtensionDataRef)) + .join(', ')}`, + ]), + ), + ]; + }, + }), + ], + }), + ]; + }, + }); + + expect(featureLoader).toBeDefined(); + expect(String(featureLoader)).toMatch( + /^FeatureLoader{description=created at '.*\/packages\/frontend-plugin-api\/src\/wiring\/createFrontendFeatureLoader\.test\.ts:.*'}$/, + ); + + await renderWithEffects( + createTestAppRoot({ + features: [featureLoader], + config: { + app: { extensions: [{ 'app/root': false }] }, + }, + }), + ); + + await expect( + screen.findByText('Names: extension-0, extension-1, extension-2'), + ).resolves.toBeInTheDocument(); + }); + + it('should guard against infinite recursion of nested feature loaders', async () => { + const nestedFeatureLoaderHolder: { + loader?: FrontendFeature | FrontendFeatureLoader; + } = {}; + const featureLoader: FrontendFeature | FrontendFeatureLoader = + createFrontendFeatureLoader({ + loader: () => + [ + nestedFeatureLoaderHolder.loader, + createFrontendPlugin({ + id: 'plugin', + extensions: [ + createExtension({ + name: 'output', + attachTo: { id: 'app', input: 'root' }, + inputs: {}, + output: [coreExtensionData.reactElement], + factory() { + return [ + coreExtensionData.reactElement( + React.createElement('span', {}, [`My Content`]), + ), + ]; + }, + }), + ], + }), + ].filter( + (f): f is FrontendFeature | FrontendFeatureLoader => + f !== undefined, + ), + }); + nestedFeatureLoaderHolder.loader = featureLoader; + + expect(featureLoader).toBeDefined(); + expect(String(featureLoader)).toMatch( + /^FeatureLoader{description=created at '.*\/packages\/frontend-plugin-api\/src\/wiring\/createFrontendFeatureLoader\.test\.ts:.*'}$/, + ); + + await renderWithEffects( + createTestAppRoot({ + features: [featureLoader], + config: { + app: { extensions: [{ 'app/root': false }] }, + }, + }), + ); + }); + + it('should support multiple output formats', async () => { + const feature = createFrontendPlugin({ + id: 'test', + }); + const dynamicFeature = Promise.resolve({ default: feature }); + + async function extractResult(f: FrontendFeature | FrontendFeatureLoader) { + const internal = f as InternalFrontendFeatureLoader; + return internal.loader({ config: mockApis.config() }); + } + + await expect( + extractResult( + createFrontendFeatureLoader({ + loader() { + return [feature]; + }, + }), + ), + ).resolves.toEqual([feature]); + + await expect( + extractResult( + createFrontendFeatureLoader({ + async loader() { + return [feature]; + }, + }), + ), + ).resolves.toEqual([feature]); + + await expect( + extractResult( + createFrontendFeatureLoader({ + *loader() { + yield feature; + }, + }), + ), + ).resolves.toEqual([feature]); + + await expect( + extractResult( + createFrontendFeatureLoader({ + async *loader() { + yield feature; + }, + }), + ), + ).resolves.toEqual([feature]); + + await expect( + extractResult( + createFrontendFeatureLoader({ + loader() { + return [dynamicFeature]; + }, + }), + ), + ).resolves.toEqual([feature]); + + await expect( + extractResult( + createFrontendFeatureLoader({ + async loader() { + return [dynamicFeature]; + }, + }), + ), + ).resolves.toEqual([feature]); + + await expect( + extractResult( + createFrontendFeatureLoader({ + *loader() { + yield dynamicFeature; + }, + }), + ), + ).resolves.toEqual([feature]); + + await expect( + extractResult( + createFrontendFeatureLoader({ + async *loader() { + yield dynamicFeature; + }, + }), + ), + ).resolves.toEqual([feature]); + }); + + it('should limit feature loading recursion', async () => { + const plugin = createFrontendPlugin({ + id: 'plugin', + extensions: [ + createExtension({ + name: 'output', + attachTo: { id: 'app', input: 'root' }, + inputs: {}, + output: [coreExtensionData.reactElement], + factory() { + return [ + coreExtensionData.reactElement( + React.createElement('span', {}, [`My Content`]), + ), + ]; + }, + }), + ], + }); + + const featureLoader: FrontendFeature | FrontendFeatureLoader = + createFrontendFeatureLoader({ + loader: () => [ + createFrontendFeatureLoader({ + loader: () => [ + createFrontendFeatureLoader({ + loader: () => [ + createFrontendFeatureLoader({ + loader: () => [ + createFrontendFeatureLoader({ + loader: () => [ + createFrontendFeatureLoader({ + loader: () => [plugin], + }), + ], + }), + ], + }), + ], + }), + ], + }), + ], + }); + + await expect( + renderWithEffects( + createTestAppRoot({ + features: [featureLoader], + config: { + app: { extensions: [{ 'app/root': false }] }, + }, + }), + ), + ).rejects.toThrow( + /^Maximum feature loading recursion depth \(5\) reached for the feature loader created at '.*\/packages\/frontend-plugin-api\/src\/wiring\/createFrontendFeatureLoader\.test\.ts:.*'$/, + ); + + const nestedLoaders = await ( + featureLoader as InternalFrontendFeatureLoader + ).loader({ config: mockApis.config() }); + await renderWithEffects( + createTestAppRoot({ + features: [nestedLoaders[0]], + config: { + app: { extensions: [{ 'app/root': false }] }, + }, + }), + ); + + await expect(screen.findByText('My Content')).resolves.toBeInTheDocument(); + }); +}); diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendFeatureLoader.ts b/packages/frontend-plugin-api/src/wiring/createFrontendFeatureLoader.ts new file mode 100644 index 0000000000..ef71a79db8 --- /dev/null +++ b/packages/frontend-plugin-api/src/wiring/createFrontendFeatureLoader.ts @@ -0,0 +1,129 @@ +/* + * 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 { ConfigApi } from '../apis/definitions'; +import { describeParentCallSite } from '../routing/describeParentCallSite'; +import { FrontendFeature } from './types'; + +/** @public */ +export interface CreateFrontendFeatureLoaderOptions { + loader(deps: { + config: ConfigApi; + }): + | Iterable< + | FrontendFeature + | FrontendFeatureLoader + | Promise<{ default: FrontendFeature | FrontendFeatureLoader }> + > + | Promise< + Iterable< + | FrontendFeature + | FrontendFeatureLoader + | Promise<{ default: FrontendFeature | FrontendFeatureLoader }> + > + > + | AsyncIterable< + | FrontendFeature + | FrontendFeatureLoader + | { default: FrontendFeature | FrontendFeatureLoader } + >; +} + +/** @public */ +export interface FrontendFeatureLoader { + readonly $$type: '@backstage/FrontendFeatureLoader'; +} + +/** @internal */ +export interface InternalFrontendFeatureLoader extends FrontendFeatureLoader { + readonly version: 'v1'; + readonly description: string; + readonly loader: (deps: { + config: ConfigApi; + }) => Promise<(FrontendFeature | FrontendFeatureLoader)[]>; +} + +/** @public */ +export function createFrontendFeatureLoader( + options: CreateFrontendFeatureLoaderOptions, +): FrontendFeatureLoader { + const description = `created at '${describeParentCallSite()}'`; + return { + $$type: '@backstage/FrontendFeatureLoader', + version: 'v1', + description, + toString() { + return `FeatureLoader{description=${description}}`; + }, + async loader(deps: { + config: ConfigApi; + }): Promise<(FrontendFeature | FrontendFeatureLoader)[]> { + const it = await options.loader(deps); + const result = new Array(); + for await (const item of it) { + if (isFeatureOrLoader(item)) { + result.push(item); + } else if ('default' in item) { + result.push(item.default); + } else { + throw new Error(`Invalid item "${item}"`); + } + } + return result; + }, + } as InternalFrontendFeatureLoader; +} + +/** @internal */ +export function isInternalFrontendFeatureLoader(opaque: { + $$type: string; +}): opaque is InternalFrontendFeatureLoader { + if (opaque.$$type === '@backstage/FrontendFeatureLoader') { + // Make sure we throw if invalid + toInternalFrontendFeatureLoader(opaque as FrontendFeatureLoader); + return true; + } + return false; +} + +/** @internal */ +export function toInternalFrontendFeatureLoader( + plugin: FrontendFeatureLoader, +): InternalFrontendFeatureLoader { + const internal = plugin as InternalFrontendFeatureLoader; + if (internal.$$type !== '@backstage/FrontendFeatureLoader') { + throw new Error(`Invalid plugin instance, bad type '${internal.$$type}'`); + } + if (internal.version !== 'v1') { + throw new Error( + `Invalid plugin instance, bad version '${internal.version}'`, + ); + } + return internal; +} + +function isFeatureOrLoader( + obj: unknown, +): obj is FrontendFeature | FrontendFeatureLoader { + if (obj !== null && typeof obj === 'object' && '$$type' in obj) { + return ( + obj.$$type === '@backstage/FrontendPlugin' || + obj.$$type === '@backstage/FrontendModule' || + obj.$$type === '@backstage/FrontendFeatureLoader' + ); + } + return false; +} diff --git a/packages/frontend-plugin-api/src/wiring/index.ts b/packages/frontend-plugin-api/src/wiring/index.ts index 6dec88b5f4..b584fc9769 100644 --- a/packages/frontend-plugin-api/src/wiring/index.ts +++ b/packages/frontend-plugin-api/src/wiring/index.ts @@ -46,15 +46,19 @@ export { type FrontendModule, type CreateFrontendModuleOptions, } from './createFrontendModule'; +export { + createFrontendFeatureLoader, + type FrontendFeatureLoader, + type CreateFrontendFeatureLoaderOptions, +} from './createFrontendFeatureLoader'; export { type Extension } from './resolveExtensionDefinition'; export { type AnyRoutes, type AnyExternalRoutes, type ExtensionDataContainer, - type ExtensionOverrides, type FeatureFlagConfig, - type FrontendFeature, type ExtensionFactoryMiddleware, + type FrontendFeature, } from './types'; export { type CreateExtensionBlueprintOptions, diff --git a/packages/frontend-plugin-api/src/wiring/types.ts b/packages/frontend-plugin-api/src/wiring/types.ts index 3aba32d9fc..22bcbdadb7 100644 --- a/packages/frontend-plugin-api/src/wiring/types.ts +++ b/packages/frontend-plugin-api/src/wiring/types.ts @@ -22,8 +22,9 @@ import { ExtensionDataRef, ExtensionDataValue, } from './createExtensionDataRef'; -import { FrontendPlugin } from './createFrontendPlugin'; import { ApiHolder, AppNode } from '../apis'; +import { FrontendModule } from './createFrontendModule'; +import { FrontendPlugin } from './createFrontendPlugin'; /** * Feature flag configuration. @@ -48,17 +49,6 @@ export type ExtensionMap< get(id: TId): TExtensionMap[TId]; }; -/** @public */ -export interface ExtensionOverrides { - readonly $$type: '@backstage/ExtensionOverrides'; -} - -/** - * @public - * @deprecated import from {@link @backstage/frontend-app-api#FrontendFeature} instead - */ -export type FrontendFeature = FrontendPlugin | ExtensionOverrides; - /** @public */ export type ExtensionDataContainer = Iterable< @@ -92,3 +82,6 @@ export type ExtensionFactoryMiddleware = ( config?: JsonObject; }, ) => Iterable>; + +/** @public */ +export type FrontendFeature = FrontendPlugin | FrontendModule; diff --git a/packages/frontend-test-utils/CHANGELOG.md b/packages/frontend-test-utils/CHANGELOG.md index b16edb4723..0442172cb9 100644 --- a/packages/frontend-test-utils/CHANGELOG.md +++ b/packages/frontend-test-utils/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/frontend-test-utils +## 0.3.0-next.2 + +### Minor Changes + +- bba525b: **BREAKING**: Removed deprecated `setupRequestMockHandlers` which was replaced by `registerMswTestHooks`. + +### Patch Changes + +- f861bfc: Added a `initialRouteEntries` option to `renderInTestApp`. +- f861bfc: The `renderInTestApp` helper now provides a default mock config with mock values for both `app.baseUrl` and `backend.baseUrl`. +- abcdf44: Internal refactor to match updated `createSpecializedApp`. +- Updated dependencies + - @backstage/frontend-app-api@0.11.0-next.2 + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-app@0.1.7-next.2 + - @backstage/test-utils@1.7.6-next.0 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + ## 0.2.7-next.1 ### Patch Changes diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index d6f98a1491..0e81615e71 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.2.7-next.1", + "version": "0.3.0-next.2", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-test-utils/report.api.md b/packages/frontend-test-utils/report.api.md index fd6aba0223..b139538cbe 100644 --- a/packages/frontend-test-utils/report.api.md +++ b/packages/frontend-test-utils/report.api.md @@ -120,13 +120,6 @@ export function renderInTestApp( options?: TestAppOptions, ): RenderResult; -// @public @deprecated (undocumented) -export function setupRequestMockHandlers(worker: { - listen: (t: any) => void; - close: () => void; - resetHandlers: () => void; -}): void; - export { TestApiProvider }; export { TestApiProviderProps }; @@ -141,6 +134,7 @@ export type TestAppOptions = { config?: JsonObject; extensions?: ExtensionDefinition[]; features?: FrontendFeature[]; + initialRouteEntries?: string[]; }; export { withLogCollector }; diff --git a/packages/frontend-test-utils/src/app/renderInTestApp.tsx b/packages/frontend-test-utils/src/app/renderInTestApp.tsx index cd9f4439b3..1643b06fac 100644 --- a/packages/frontend-test-utils/src/app/renderInTestApp.tsx +++ b/packages/frontend-test-utils/src/app/renderInTestApp.tsx @@ -36,6 +36,11 @@ import { } from '@backstage/frontend-plugin-api'; import appPlugin from '@backstage/plugin-app'; +const DEFAULT_MOCK_CONFIG = { + app: { baseUrl: 'http://localhost:3000' }, + backend: { baseUrl: 'http://localhost:7007' }, +}; + /** * Options to customize the behavior of the test app. * @public @@ -73,6 +78,11 @@ export type TestAppOptions = { * Additional features to add to the test app. */ features?: FrontendFeature[]; + + /** + * Initial route entries to use for the router. + */ + initialRouteEntries?: string[]; }; const NavItem = (props: { @@ -150,7 +160,11 @@ export function renderInTestApp( }), RouterBlueprint.make({ params: { - Component: ({ children }) => {children}, + Component: ({ children }) => ( + + {children} + + ), }, }), ]; @@ -197,7 +211,10 @@ export function renderInTestApp( const app = createSpecializedApp({ features, config: ConfigReader.fromConfigs([ - { context: 'render-config', data: options?.config ?? {} }, + { + context: 'render-config', + data: options?.config ?? DEFAULT_MOCK_CONFIG, + }, ]), }); diff --git a/packages/frontend-test-utils/src/index.ts b/packages/frontend-test-utils/src/index.ts index 20f393706e..cab66895e0 100644 --- a/packages/frontend-test-utils/src/index.ts +++ b/packages/frontend-test-utils/src/index.ts @@ -20,7 +20,6 @@ * Contains utilities that can be used when testing frontend features such as extensions. */ -export * from './deprecated'; export * from './apis'; export * from './app'; diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index 1b9f5732de..8d4d636a7b 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/integration-react +## 1.2.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + ## 1.2.4 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index 163b0338d8..150537049b 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration-react", - "version": "1.2.4", + "version": "1.2.5-next.0", "description": "Frontend package for managing integrations towards external systems", "backstage": { "role": "web-library" diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index 3e9c912904..9f94a068c8 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/integration +## 1.16.2-next.0 + +### Patch Changes + +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 1.16.1 ### Patch Changes diff --git a/packages/integration/config.d.ts b/packages/integration/config.d.ts index 1e4c8d373d..bd5267ab3d 100644 --- a/packages/integration/config.d.ts +++ b/packages/integration/config.d.ts @@ -62,6 +62,11 @@ export interface Config { tenantId?: string; personalAccessToken?: string; }[]; + /** + * PGP signing key for signing commits. + * @visibility secret + */ + commitSigningKey?: string; }>; /** @@ -94,6 +99,11 @@ export interface Config { * @visibility secret */ appPassword?: string; + /** + * PGP signing key for signing commits. + * @visibility secret + */ + commitSigningKey?: string; }>; /** Integration configuration for Bitbucket Cloud */ @@ -108,6 +118,11 @@ export interface Config { * @visibility secret */ appPassword: string; + /** + * PGP signing key for signing commits. + * @visibility secret + */ + commitSigningKey?: string; }>; /** Integration configuration for Bitbucket Server */ @@ -137,6 +152,11 @@ export interface Config { * @visibility frontend */ apiBaseUrl?: string; + /** + * PGP signing key for signing commits. + * @visibility secret + */ + commitSigningKey?: string; }>; /** Integration configuration for Gerrit */ @@ -172,6 +192,11 @@ export interface Config { * @visibility secret */ password?: string; + /** + * PGP signing key for signing commits. + * @visibility secret + */ + commitSigningKey?: string; }>; /** Integration configuration for GitHub */ @@ -269,6 +294,11 @@ export interface Config { * @visibility frontend */ baseUrl?: string; + /** + * PGP signing key for signing commits. + * @visibility secret + */ + commitSigningKey?: string; }>; /** Integration configuration for Google Cloud Storage */ @@ -349,6 +379,11 @@ export interface Config { * @visibility secret */ password?: string; + /** + * PGP signing key for signing commits. + * @visibility secret + */ + commitSigningKey?: string; }>; /** Integration configuration for Harness Code */ harness?: Array<{ diff --git a/packages/integration/package.json b/packages/integration/package.json index a51850ed9c..121c297e44 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration", - "version": "1.16.1", + "version": "1.16.2-next.0", "description": "Helpers for managing integrations towards external systems", "backstage": { "role": "common-library" diff --git a/packages/integration/report.api.md b/packages/integration/report.api.md index 6bb48b8185..e44b02851c 100644 --- a/packages/integration/report.api.md +++ b/packages/integration/report.api.md @@ -201,6 +201,7 @@ export type AzureIntegrationConfig = { token?: string; credential?: AzureDevOpsCredential; credentials?: AzureDevOpsCredential[]; + commitSigningKey?: string; }; // @public @@ -237,6 +238,7 @@ export type BitbucketCloudIntegrationConfig = { username?: string; appPassword?: string; token?: string; + commitSigningKey?: string; }; // @public @deprecated @@ -267,6 +269,7 @@ export type BitbucketIntegrationConfig = { token?: string; username?: string; appPassword?: string; + commitSigningKey?: string; }; // @public @@ -297,6 +300,7 @@ export type BitbucketServerIntegrationConfig = { token?: string; username?: string; password?: string; + commitSigningKey?: string; }; // @public @deprecated @@ -395,6 +399,7 @@ export type GerritIntegrationConfig = { gitilesBaseUrl: string; username?: string; password?: string; + commitSigningKey?: string; }; // @public @@ -630,6 +635,7 @@ export type GiteaIntegrationConfig = { baseUrl?: string; username?: string; password?: string; + commitSigningKey?: string; }; // @public @@ -744,6 +750,7 @@ export type GitLabIntegrationConfig = { apiBaseUrl: string; token?: string; baseUrl: string; + commitSigningKey?: string; }; // @public diff --git a/packages/integration/src/azure/config.ts b/packages/integration/src/azure/config.ts index 5ed1914ea2..2910387829 100644 --- a/packages/integration/src/azure/config.ts +++ b/packages/integration/src/azure/config.ts @@ -57,6 +57,11 @@ export type AzureIntegrationConfig = { * If no credentials are specified at all, either a default credential (for Azure DevOps) or anonymous access (for Azure DevOps Server) is used. */ credentials?: AzureDevOpsCredential[]; + + /** + * Signing key for commits + */ + commitSigningKey?: string; }; /** @@ -349,6 +354,7 @@ export function readAzureIntegrationConfig( return { host, credentials, + commitSigningKey: config.getOptionalString('commitSigningKey'), }; } diff --git a/packages/integration/src/bitbucket/config.ts b/packages/integration/src/bitbucket/config.ts index 4b6296b0e9..6939034e7d 100644 --- a/packages/integration/src/bitbucket/config.ts +++ b/packages/integration/src/bitbucket/config.ts @@ -62,6 +62,11 @@ export type BitbucketIntegrationConfig = { * See https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/ */ appPassword?: string; + + /** + * Signing key for commits + */ + commitSigningKey?: string; }; /** @@ -100,6 +105,7 @@ export function readBitbucketIntegrationConfig( token, username, appPassword, + commitSigningKey: config.getOptionalString('commitSigningKey'), }; } diff --git a/packages/integration/src/bitbucketCloud/config.ts b/packages/integration/src/bitbucketCloud/config.ts index 32a2af9313..eb4b5c4904 100644 --- a/packages/integration/src/bitbucketCloud/config.ts +++ b/packages/integration/src/bitbucketCloud/config.ts @@ -51,6 +51,9 @@ export type BitbucketCloudIntegrationConfig = { * The access token to use for requests to Bitbucket Cloud (bitbucket.org). */ token?: string; + + /** PGP private key for signing commits. */ + commitSigningKey?: string; }; /** @@ -74,6 +77,7 @@ export function readBitbucketCloudIntegrationConfig( apiBaseUrl, username, appPassword, + commitSigningKey: config.getOptionalString('commitSigningKey'), }; } diff --git a/packages/integration/src/bitbucketServer/config.ts b/packages/integration/src/bitbucketServer/config.ts index 1412b7ab17..b2a243b3b3 100644 --- a/packages/integration/src/bitbucketServer/config.ts +++ b/packages/integration/src/bitbucketServer/config.ts @@ -64,6 +64,11 @@ export type BitbucketServerIntegrationConfig = { * See https://developer.atlassian.com/server/bitbucket/how-tos/command-line-rest/#authentication */ password?: string; + + /** + * Signing key for commits + */ + commitSigningKey?: string; }; /** @@ -99,6 +104,7 @@ export function readBitbucketServerIntegrationConfig( token, username, password, + commitSigningKey: config.getOptionalString('commitSigningKey'), }; } diff --git a/packages/integration/src/gerrit/config.ts b/packages/integration/src/gerrit/config.ts index a5fdc8dabf..fe88750687 100644 --- a/packages/integration/src/gerrit/config.ts +++ b/packages/integration/src/gerrit/config.ts @@ -60,6 +60,11 @@ export type GerritIntegrationConfig = { * The password or http token to use for authentication. */ password?: string; + + /** + * The signing key to use for signing commits. + */ + commitSigningKey?: string; }; /** @@ -119,6 +124,7 @@ export function readGerritIntegrationConfig( gitilesBaseUrl, username, password, + commitSigningKey: config.getOptionalString('commitSigningKey'), }; } diff --git a/packages/integration/src/gitea/config.ts b/packages/integration/src/gitea/config.ts index ae1fe5ee87..461ee60715 100644 --- a/packages/integration/src/gitea/config.ts +++ b/packages/integration/src/gitea/config.ts @@ -45,6 +45,11 @@ export type GiteaIntegrationConfig = { * The password or http token to use for authentication. */ password?: string; + + /** + * Signing key to to sign commits + */ + commitSigningKey?: string; }; /** @@ -79,5 +84,6 @@ export function readGiteaConfig(config: Config): GiteaIntegrationConfig { baseUrl, username, password, + commitSigningKey: config.getOptionalString('commitSigningKey'), }; } diff --git a/packages/integration/src/gitlab/config.ts b/packages/integration/src/gitlab/config.ts index 79161561f3..636919291c 100644 --- a/packages/integration/src/gitlab/config.ts +++ b/packages/integration/src/gitlab/config.ts @@ -54,6 +54,11 @@ export type GitLabIntegrationConfig = { * If no baseUrl is provided, it will default to `https://${host}` */ baseUrl: string; + + /** + * Signing key to sign commits + */ + commitSigningKey?: string; }; /** @@ -95,7 +100,13 @@ export function readGitLabIntegrationConfig( ); } - return { host, token, apiBaseUrl, baseUrl }; + return { + host, + token, + apiBaseUrl, + baseUrl, + commitSigningKey: config.getOptionalString('commitSigningKey'), + }; } /** diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index 1929807367..7c9c722d08 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/repo-tools +## 0.13.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.0-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/errors@1.2.7 + ## 0.13.1-next.1 ### Patch Changes diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index f352917dd4..c0f6f68773 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/repo-tools", - "version": "0.13.1-next.1", + "version": "0.13.1-next.2", "description": "CLI for Backstage repo tooling ", "backstage": { "role": "cli" diff --git a/packages/scaffolder-internal/CHANGELOG.md b/packages/scaffolder-internal/CHANGELOG.md index 47d8472a1d..76f68005a4 100644 --- a/packages/scaffolder-internal/CHANGELOG.md +++ b/packages/scaffolder-internal/CHANGELOG.md @@ -1,5 +1,13 @@ # @internal/scaffolder +## 0.0.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-scaffolder-react@1.14.6-next.2 + ## 0.0.7-next.1 ### Patch Changes diff --git a/packages/scaffolder-internal/package.json b/packages/scaffolder-internal/package.json index 10d44a9555..11fd798a8b 100644 --- a/packages/scaffolder-internal/package.json +++ b/packages/scaffolder-internal/package.json @@ -1,6 +1,6 @@ { "name": "@internal/scaffolder", - "version": "0.0.7-next.1", + "version": "0.0.7-next.2", "backstage": { "role": "web-library", "inline": true diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index 35b7dd86f6..4dc3a48f7c 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,24 @@ # techdocs-cli-embedded-app +## 0.2.106-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.28.0-next.2 + - @backstage/cli@0.31.0-next.1 + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/core-components@0.16.5-next.1 + - @backstage/app-defaults@1.6.0-next.1 + - @backstage/test-utils@1.7.6-next.0 + - @backstage/plugin-techdocs@1.12.4-next.2 + - @backstage/plugin-techdocs-react@1.2.15-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/theme@0.6.4 + ## 0.2.106-next.1 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index e40215eb96..14dce40c95 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.106-next.1", + "version": "0.2.106-next.2", "backstage": { "role": "frontend" }, diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 19b44d7d9d..2c01a023ae 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,16 @@ # @techdocs/cli +## 1.9.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.13.1-next.2 + ## 1.9.1-next.1 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 283035dd97..580d88a8ce 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,6 +1,6 @@ { "name": "@techdocs/cli", - "version": "1.9.1-next.1", + "version": "1.9.1-next.2", "description": "Utility CLI for managing TechDocs sites in Backstage.", "backstage": { "role": "cli" diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index 8ec67ed51b..90d90a46f9 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/test-utils +## 1.7.6-next.0 + +### Patch Changes + +- 37c6510: Moved `@types/jest` to `devDependencies`. +- Updated dependencies + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-react@0.4.31 + ## 1.7.5 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 5d97304350..f23f6902fd 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/test-utils", - "version": "1.7.5", + "version": "1.7.6-next.0", "description": "Utilities to test Backstage plugins and apps.", "backstage": { "role": "web-library" diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 4f7d91ff35..6117c0bd5e 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-api-docs +## 0.12.5-next.2 + +### Patch Changes + +- 74871cc: Use consistent Typography in Entity HasApisCard +- Updated dependencies + - @backstage/plugin-catalog@1.28.0-next.2 + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-react@0.4.31 + ## 0.12.5-next.1 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 80890155a7..d8cbb3dd8e 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.12.5-next.1", + "version": "0.12.5-next.2", "description": "A Backstage plugin that helps represent API entities in the frontend", "backstage": { "role": "frontend-plugin", diff --git a/plugins/api-docs/report-alpha.api.md b/plugins/api-docs/report-alpha.api.md index 0108d94e49..25fda584e1 100644 --- a/plugins/api-docs/report-alpha.api.md +++ b/plugins/api-docs/report-alpha.api.md @@ -48,11 +48,11 @@ const _default: FrontendPlugin< name: 'consumed-apis'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef< @@ -93,11 +93,11 @@ const _default: FrontendPlugin< name: 'consuming-components'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef< @@ -138,11 +138,11 @@ const _default: FrontendPlugin< name: 'definition'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef< @@ -183,11 +183,11 @@ const _default: FrontendPlugin< name: 'has-apis'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef< @@ -228,11 +228,11 @@ const _default: FrontendPlugin< name: 'provided-apis'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef< @@ -273,11 +273,11 @@ const _default: FrontendPlugin< name: 'providing-components'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef< diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 4fe5387594..67a6b900d8 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-app-backend +## 0.5.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.0-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-app-node@0.1.31-next.2 + - @backstage/plugin-auth-node@0.6.1-next.1 + ## 0.5.0-next.1 ### Minor Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 9b6526be4e..4cf444996b 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.5.0-next.1", + "version": "0.5.0-next.2", "description": "A Backstage backend plugin that serves the Backstage frontend app", "backstage": { "role": "backend-plugin", diff --git a/plugins/app-backend/src/setupTests.ts b/plugins/app-backend/src/setupTests.ts index 4ed508a382..ea10a94839 100644 --- a/plugins/app-backend/src/setupTests.ts +++ b/plugins/app-backend/src/setupTests.ts @@ -17,5 +17,5 @@ import { TestDatabases } from '@backstage/backend-test-utils'; TestDatabases.setDefaults({ - ids: ['MYSQL_8', 'POSTGRES_16', 'POSTGRES_12', 'SQLITE_3'], + ids: ['MYSQL_8', 'POSTGRES_17', 'POSTGRES_13', 'SQLITE_3'], }); diff --git a/plugins/app-node/CHANGELOG.md b/plugins/app-node/CHANGELOG.md index 08ded25c6f..1b5fd25b6a 100644 --- a/plugins/app-node/CHANGELOG.md +++ b/plugins/app-node/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-app-node +## 0.1.31-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.0-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + ## 0.1.31-next.1 ### Patch Changes diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json index 1a782cbac4..b178c5079b 100644 --- a/plugins/app-node/package.json +++ b/plugins/app-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-node", - "version": "0.1.31-next.1", + "version": "0.1.31-next.2", "description": "Node.js library for the app plugin", "backstage": { "role": "node-library", diff --git a/plugins/app-visualizer/CHANGELOG.md b/plugins/app-visualizer/CHANGELOG.md index fbd2003184..e89d604c34 100644 --- a/plugins/app-visualizer/CHANGELOG.md +++ b/plugins/app-visualizer/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-app-visualizer +## 0.1.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + ## 0.1.17-next.1 ### Patch Changes diff --git a/plugins/app-visualizer/package.json b/plugins/app-visualizer/package.json index 7f52bb2d5b..1b924d529d 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.17-next.1", + "version": "0.1.17-next.2", "description": "Visualizes the Backstage app structure", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app/CHANGELOG.md b/plugins/app/CHANGELOG.md index 886d02e9aa..1dad673c93 100644 --- a/plugins/app/CHANGELOG.md +++ b/plugins/app/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-app +## 0.1.7-next.2 + +### Patch Changes + +- 0aa9d82: Added implementation of the new `DialogApi`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-react@0.4.31 + ## 0.1.7-next.1 ### Patch Changes diff --git a/plugins/app/package.json b/plugins/app/package.json index 86007ac2d0..74fa075e77 100644 --- a/plugins/app/package.json +++ b/plugins/app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app", - "version": "0.1.7-next.1", + "version": "0.1.7-next.2", "backstage": { "role": "frontend-plugin", "pluginId": "app", diff --git a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md index a732b9100e..246a101f14 100644 --- a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-aws-alb-provider +## 0.4.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-backend@0.24.4-next.2 + - @backstage/plugin-auth-node@0.6.1-next.1 + ## 0.4.1-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-aws-alb-provider/package.json b/plugins/auth-backend-module-aws-alb-provider/package.json index a2905159f8..0eba4cb971 100644 --- a/plugins/auth-backend-module-aws-alb-provider/package.json +++ b/plugins/auth-backend-module-aws-alb-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-aws-alb-provider", - "version": "0.4.1-next.1", + "version": "0.4.1-next.2", "description": "The aws-alb provider module for the Backstage auth backend.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md index faf545c9f9..9317b326ff 100644 --- a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-oauth2-provider +## 0.4.1-next.2 + +### Patch Changes + +- ce15e30: Fixed repository url in `README.md` +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + ## 0.4.1-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-provider/README.md b/plugins/auth-backend-module-oauth2-provider/README.md index 6c23d24ff3..1a6937515e 100644 --- a/plugins/auth-backend-module-oauth2-provider/README.md +++ b/plugins/auth-backend-module-oauth2-provider/README.md @@ -4,5 +4,5 @@ This module provides an oauth2 auth provider implementation for `@backstage/plug ## Links -- [Repository](https://oauth2.com/backstage/backstage/tree/master/plugins/auth-backend-module-oauth2-provider) +- [Repository](https://github.com/backstage/backstage/tree/master/plugins/auth-backend-module-oauth2-provider) - [Backstage Project Homepage](https://backstage.io) diff --git a/plugins/auth-backend-module-oauth2-provider/package.json b/plugins/auth-backend-module-oauth2-provider/package.json index e8dcccd33a..c83d99a276 100644 --- a/plugins/auth-backend-module-oauth2-provider/package.json +++ b/plugins/auth-backend-module-oauth2-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-provider", - "version": "0.4.1-next.1", + "version": "0.4.1-next.2", "description": "The oauth2-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md index 8807ae4100..32d0961094 100644 --- a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-oidc-provider +## 0.4.1-next.2 + +### Patch Changes + +- ce15e30: Fixed repository url in `README.md` +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/plugin-auth-backend@0.24.4-next.2 + - @backstage/plugin-auth-node@0.6.1-next.1 + ## 0.4.1-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-oidc-provider/README.md b/plugins/auth-backend-module-oidc-provider/README.md index e586f32fd7..1cbe3525cd 100644 --- a/plugins/auth-backend-module-oidc-provider/README.md +++ b/plugins/auth-backend-module-oidc-provider/README.md @@ -4,5 +4,5 @@ This module provides an Oidc auth provider implementation for `@backstage/plugin ## Links -- [Repository](https://oidc.com/backstage/backstage/tree/master/plugins/auth-backend-module-oidc-provider) +- [Repository](https://github.com/backstage/backstage/tree/master/plugins/auth-backend-module-oidc-provider) - [Backstage Project Homepage](https://backstage.io) diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index e5cb3b6d9e..3de2277163 100644 --- a/plugins/auth-backend-module-oidc-provider/package.json +++ b/plugins/auth-backend-module-oidc-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oidc-provider", - "version": "0.4.1-next.1", + "version": "0.4.1-next.2", "description": "The oidc-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-okta-provider/CHANGELOG.md b/plugins/auth-backend-module-okta-provider/CHANGELOG.md index 2d77340cc5..0e33cc5451 100644 --- a/plugins/auth-backend-module-okta-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-okta-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-okta-provider +## 0.2.1-next.2 + +### Patch Changes + +- ce15e30: Fixed repository url in `README.md` +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + ## 0.2.1-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-okta-provider/README.md b/plugins/auth-backend-module-okta-provider/README.md index 3a320a9b1a..51aa13c47e 100644 --- a/plugins/auth-backend-module-okta-provider/README.md +++ b/plugins/auth-backend-module-okta-provider/README.md @@ -26,5 +26,5 @@ export const okta = createAuthProviderIntegration({ ## Links -- [Repository](https://okta.com/backstage/backstage/tree/master/plugins/auth-backend-module-okta-provider) +- [Repository](https://github.com/backstage/backstage/tree/master/plugins/auth-backend-module-okta-provider) - [Backstage Project Homepage](https://backstage.io) diff --git a/plugins/auth-backend-module-okta-provider/package.json b/plugins/auth-backend-module-okta-provider/package.json index 86c3041f4d..8ac9af58cd 100644 --- a/plugins/auth-backend-module-okta-provider/package.json +++ b/plugins/auth-backend-module-okta-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-okta-provider", - "version": "0.2.1-next.1", + "version": "0.2.1-next.2", "description": "The okta-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 db43089c46..8a2893bffc 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,36 @@ # @backstage/plugin-auth-backend +## 0.24.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend-module-oauth2-provider@0.4.1-next.2 + - @backstage/plugin-auth-backend-module-oidc-provider@0.4.1-next.2 + - @backstage/plugin-auth-backend-module-okta-provider@0.2.1-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.4.1-next.1 + - @backstage/plugin-auth-backend-module-auth0-provider@0.2.1-next.1 + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.4.1-next.2 + - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.6-next.1 + - @backstage/plugin-auth-backend-module-bitbucket-provider@0.3.1-next.1 + - @backstage/plugin-auth-backend-module-bitbucket-server-provider@0.2.1-next.1 + - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.4.1-next.1 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.4.1-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.3.1-next.1 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.3.1-next.1 + - @backstage/plugin-auth-backend-module-google-provider@0.3.1-next.1 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.3.1-next.1 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.6-next.1 + - @backstage/plugin-auth-backend-module-onelogin-provider@0.3.1-next.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-catalog-node@1.16.1-next.1 + ## 0.24.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index d971a0f6b0..46a4e04364 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.24.4-next.1", + "version": "0.24.4-next.2", "description": "A Backstage backend plugin that handles authentication", "backstage": { "role": "backend-plugin", diff --git a/plugins/auth-backend/src/setupTests.ts b/plugins/auth-backend/src/setupTests.ts index f7c56ef27d..5af5f05360 100644 --- a/plugins/auth-backend/src/setupTests.ts +++ b/plugins/auth-backend/src/setupTests.ts @@ -19,5 +19,5 @@ import { TestDatabases } from '@backstage/backend-test-utils'; export {}; TestDatabases.setDefaults({ - ids: ['MYSQL_8', 'POSTGRES_16', 'POSTGRES_12', 'SQLITE_3'], + ids: ['MYSQL_8', 'POSTGRES_17', 'POSTGRES_13', 'SQLITE_3'], }); diff --git a/plugins/auth-react/CHANGELOG.md b/plugins/auth-react/CHANGELOG.md index 762227a2e8..10cb852de8 100644 --- a/plugins/auth-react/CHANGELOG.md +++ b/plugins/auth-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-react +## 0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + ## 0.1.13-next.0 ### Patch Changes diff --git a/plugins/auth-react/package.json b/plugins/auth-react/package.json index 7a008cd28b..d30e73ed2c 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.13-next.0", + "version": "0.1.13-next.1", "description": "Web library for the auth plugin", "backstage": { "role": "web-library", diff --git a/plugins/bitbucket-cloud-common/CHANGELOG.md b/plugins/bitbucket-cloud-common/CHANGELOG.md index a7922129cf..c298b964e0 100644 --- a/plugins/bitbucket-cloud-common/CHANGELOG.md +++ b/plugins/bitbucket-cloud-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-bitbucket-cloud-common +## 0.2.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + ## 0.2.27 ### Patch Changes diff --git a/plugins/bitbucket-cloud-common/package.json b/plugins/bitbucket-cloud-common/package.json index ffc2f097b0..d279781744 100644 --- a/plugins/bitbucket-cloud-common/package.json +++ b/plugins/bitbucket-cloud-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bitbucket-cloud-common", - "version": "0.2.27", + "version": "0.2.28-next.0", "description": "Common functionalities for bitbucket-cloud plugins", "backstage": { "role": "common-library", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index b670e59d45..80b26f796f 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.4.9-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.15 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-kubernetes-common@0.9.4-next.0 + ## 0.4.9-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index 03679af187..afcb5dfba2 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.4.9-next.1", + "version": "0.4.9-next.2", "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 2011ebce75..ae84239ea5 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.3.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + ## 0.3.3-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index b8d5ceb1bc..d14317c251 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.3.3-next.1", + "version": "0.3.3-next.2", "description": "A Backstage catalog backend module that helps integrate towards Azure", "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 553d1ffd4d..69132a9095 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.4.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-bitbucket-cloud-common@0.2.28-next.0 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + ## 0.4.6-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index d014fb3f5d..647d77f7cf 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", - "version": "0.4.6-next.1", + "version": "0.4.6-next.2", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index 18074e247c..0447d8afb1 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.3.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-node@1.16.1-next.1 + ## 0.3.3-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 2fa3b67510..245d4ed3f2 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.3.3-next.1", + "version": "0.3.3-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index 8010ff9a4c..c916830d5e 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.2.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-node@1.16.1-next.1 + ## 0.2.8-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index cf2c9a7f69..4c97d0f621 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.2.8-next.1", + "version": "0.2.8-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-github-org/CHANGELOG.md b/plugins/catalog-backend-module-github-org/CHANGELOG.md index aa55fde053..ad5726a01d 100644 --- a/plugins/catalog-backend-module-github-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-github-org/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-github-org +## 0.3.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-backend-module-github@0.7.11-next.2 + - @backstage/plugin-catalog-node@1.16.1-next.1 + ## 0.3.8-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-github-org/package.json b/plugins/catalog-backend-module-github-org/package.json index 9726d6234d..34b4a647a6 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.3.8-next.1", + "version": "0.3.8-next.2", "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 c30eedc661..b4eda44153 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-backend-module-github +## 0.7.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.32.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + ## 0.7.11-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index 37fa762684..fb92604868 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.7.11-next.1", + "version": "0.7.11-next.2", "description": "A Backstage catalog backend module that helps integrate towards GitHub", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md index 0b32b8ead8..efe993b351 100644 --- a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend-module-gitlab-org +## 0.2.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/plugin-catalog-backend-module-gitlab@0.6.4-next.2 + - @backstage/plugin-catalog-node@1.16.1-next.1 + ## 0.2.7-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab-org/package.json b/plugins/catalog-backend-module-gitlab-org/package.json index 3f1761fc1e..aae1f2afd6 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.2.7-next.1", + "version": "0.2.7-next.2", "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 9d22620a51..719d614474 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.6.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + ## 0.6.4-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index ae29409cb0..3c005aba46 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", - "version": "0.6.4-next.1", + "version": "0.6.4-next.2", "description": "A Backstage catalog backend module that helps integrate towards GitLab", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index eda4c60f2c..fba30c8ede 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md +++ b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-incremental-ingestion +## 0.6.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.32.0-next.2 + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-permission-common@0.8.4 + ## 0.6.4-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index b3515b63f8..0e2811b3d7 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.6.4-next.1", + "version": "0.6.4-next.2", "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.test.ts b/plugins/catalog-backend-module-incremental-ingestion/src/database/IncrementalIngestionDatabaseManager.test.ts index a8862cfc2a..bed216b5de 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/database/IncrementalIngestionDatabaseManager.test.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/database/IncrementalIngestionDatabaseManager.test.ts @@ -24,7 +24,7 @@ jest.setTimeout(60_000); describe('IncrementalIngestionDatabaseManager', () => { const databases = TestDatabases.create({ - ids: ['POSTGRES_13', 'POSTGRES_9', 'SQLITE_3'], + ids: ['POSTGRES_17', 'POSTGRES_13', 'SQLITE_3'], }); it.each(databases.eachSupportedId())( diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/module/WrapperProviders.test.ts b/plugins/catalog-backend-module-incremental-ingestion/src/module/WrapperProviders.test.ts index 95b3f203c4..877e32acfb 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/module/WrapperProviders.test.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/module/WrapperProviders.test.ts @@ -25,7 +25,7 @@ jest.setTimeout(60_000); describe('WrapperProviders', () => { const applyDatabaseMigrations = jest.fn(); const databases = TestDatabases.create({ - ids: ['POSTGRES_16', 'POSTGRES_12', 'SQLITE_3', 'MYSQL_8'], + ids: ['POSTGRES_17', 'POSTGRES_13', 'SQLITE_3', 'MYSQL_8'], }); const config = new ConfigReader({}); const logger = mockServices.logger.mock(); diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 1b7986cc0d..058e8d215a 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.11.3-next.2 + +### Patch Changes + +- e43f41b: Fix `config.d.ts` for `ldapOrg` being incorrect. The documentation says a single + object or an array are accepted, but the definition only allows an object. +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + ## 0.11.3-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index b05c573c21..488f5d625e 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.11.3-next.1", + "version": "0.11.3-next.2", "description": "A Backstage catalog backend module that helps integrate towards LDAP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-logs/CHANGELOG.md b/plugins/catalog-backend-module-logs/CHANGELOG.md index f876afe4c1..81985ea512 100644 --- a/plugins/catalog-backend-module-logs/CHANGELOG.md +++ b/plugins/catalog-backend-module-logs/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend-module-logs +## 0.1.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.32.0-next.2 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + ## 0.1.8-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-logs/package.json b/plugins/catalog-backend-module-logs/package.json index aa6de8ef45..b40c106851 100644 --- a/plugins/catalog-backend-module-logs/package.json +++ b/plugins/catalog-backend-module-logs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-logs", - "version": "0.1.8-next.1", + "version": "0.1.8-next.2", "description": "A module that subscribes to catalog releated events and logs them.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index 105f93513a..33cd7610c2 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.2.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + ## 0.2.8-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index a687e83c20..59307ffc4f 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.2.8-next.1", + "version": "0.2.8-next.2", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index a93e5e663a..ca1305c43b 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-catalog-backend +## 1.32.0-next.2 + +### Patch Changes + +- 4306303: Added a fix in `@backstage/plugin-catalog-backend` to prevent duplicate path keys in entity search if only casing is different. +- 5243aa4: Fixed an issue occurred when authorizing permissions using custom rules passed via the `PermissionsRegistryService`. +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-openapi-utils@0.5.1-next.1 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-node@0.8.9-next.1 + - @backstage/plugin-search-backend-module-catalog@0.3.2-next.1 + - @backstage/plugin-search-common@1.2.17 + ## 1.32.0-next.1 ### Minor Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index b62b3d89f2..02f4878304 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "1.32.0-next.1", + "version": "1.32.0-next.2", "description": "The Backstage backend plugin that provides the Backstage catalog", "backstage": { "role": "backend-plugin", diff --git a/plugins/catalog-backend/src/setupTests.ts b/plugins/catalog-backend/src/setupTests.ts index f7c56ef27d..5af5f05360 100644 --- a/plugins/catalog-backend/src/setupTests.ts +++ b/plugins/catalog-backend/src/setupTests.ts @@ -19,5 +19,5 @@ import { TestDatabases } from '@backstage/backend-test-utils'; export {}; TestDatabases.setDefaults({ - ids: ['MYSQL_8', 'POSTGRES_16', 'POSTGRES_12', 'SQLITE_3'], + ids: ['MYSQL_8', 'POSTGRES_17', 'POSTGRES_13', 'SQLITE_3'], }); diff --git a/plugins/catalog-backend/src/tests/performance/getEntitiesPerformance.test.ts b/plugins/catalog-backend/src/tests/performance/getEntitiesPerformance.test.ts index 9d1bb8d80a..aa76bace54 100644 --- a/plugins/catalog-backend/src/tests/performance/getEntitiesPerformance.test.ts +++ b/plugins/catalog-backend/src/tests/performance/getEntitiesPerformance.test.ts @@ -40,7 +40,7 @@ import { describePerformanceTest, performanceTraceEnabled } from './lib/env'; jest.setTimeout(600_000); const databases = TestDatabases.create({ - ids: [/* 'MYSQL_8', */ 'POSTGRES_16', /* 'POSTGRES_12',*/ 'SQLITE_3'], + ids: [/* 'MYSQL_8', */ 'POSTGRES_17', /* 'POSTGRES_13',*/ 'SQLITE_3'], disableDocker: false, }); diff --git a/plugins/catalog-backend/src/tests/performance/getProcessableEntitiesPerformance.test.ts b/plugins/catalog-backend/src/tests/performance/getProcessableEntitiesPerformance.test.ts index 8218ae82cc..e2ed160b42 100644 --- a/plugins/catalog-backend/src/tests/performance/getProcessableEntitiesPerformance.test.ts +++ b/plugins/catalog-backend/src/tests/performance/getProcessableEntitiesPerformance.test.ts @@ -25,7 +25,7 @@ import { describePerformanceTest, performanceTraceEnabled } from './lib/env'; jest.setTimeout(600_000); const databases = TestDatabases.create({ - ids: [/* 'MYSQL_8', */ 'POSTGRES_16', /* 'POSTGRES_12',*/ 'SQLITE_3'], + ids: [/* 'MYSQL_8', */ 'POSTGRES_17', /* 'POSTGRES_13',*/ 'SQLITE_3'], disableDocker: false, }); diff --git a/plugins/catalog-backend/src/tests/performance/providerDeltaMutations.test.ts b/plugins/catalog-backend/src/tests/performance/providerDeltaMutations.test.ts index 6e95c2535a..377843643b 100644 --- a/plugins/catalog-backend/src/tests/performance/providerDeltaMutations.test.ts +++ b/plugins/catalog-backend/src/tests/performance/providerDeltaMutations.test.ts @@ -26,7 +26,7 @@ import { DeferredEntity } from '@backstage/plugin-catalog-node'; jest.setTimeout(600_000); const databases = TestDatabases.create({ - ids: [/* 'MYSQL_8', */ 'POSTGRES_16', /* 'POSTGRES_12',*/ 'SQLITE_3'], + ids: [/* 'MYSQL_8', */ 'POSTGRES_17', /* 'POSTGRES_13',*/ 'SQLITE_3'], disableDocker: false, }); diff --git a/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts b/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts index a4d843649e..d0bf7c2cd8 100644 --- a/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts +++ b/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts @@ -143,7 +143,7 @@ class Tracker { describePerformanceTest('stitchingPerformance', () => { const databases = TestDatabases.create({ - ids: [/* 'MYSQL_8', */ 'POSTGRES_16', 'POSTGRES_12', 'SQLITE_3'], + ids: [/* 'MYSQL_8', */ 'POSTGRES_17', 'POSTGRES_13', 'SQLITE_3'], }); it.each(databases.eachSupportedId())( diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index 0be8f15993..0da3f48885 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-graph +## 0.4.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/types@1.2.1 + ## 0.4.17-next.1 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index ac54e13330..3d9bdb96c1 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.17-next.1", + "version": "0.4.17-next.2", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-graph", diff --git a/plugins/catalog-graph/report-alpha.api.md b/plugins/catalog-graph/report-alpha.api.md index 4cc884e7d7..0687efce05 100644 --- a/plugins/catalog-graph/report-alpha.api.md +++ b/plugins/catalog-graph/report-alpha.api.md @@ -45,7 +45,7 @@ const _default: FrontendPlugin< height: number | undefined; } & { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { height?: number | undefined; @@ -61,7 +61,7 @@ const _default: FrontendPlugin< relationPairs?: [string, string][] | undefined; } & { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef< diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index a6c8a4725d..0760281898 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-import +## 0.12.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/integration@1.16.2-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/plugin-catalog-common@1.1.3 + ## 0.12.11-next.1 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 05ce3a5982..49eaed96f8 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.12.11-next.1", + "version": "0.12.11-next.2", "description": "A Backstage plugin the helps you import entities into your catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index b925168e39..adcacbc699 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,42 @@ # @backstage/plugin-catalog-react +## 1.16.0-next.2 + +### Minor Changes + +- 7f57365: Add support for a new entity predicate syntax when defining `filter`s related to the blueprints exported via `/alpha` for the new frontend system. For more information, see the [entity filters documentation](https://backstage.io/docs/features/software-catalog/catalog-customization#advanced-customization#entity-filters). +- 247a40b: Introduces a new `EntityHeaderBlueprint` that allows you to override the default entity page header. + + ```jsx + import { EntityHeaderBlueprint } from '@backstage/plugin-catalog-react/alpha'; + + EntityHeaderBlueprint.make({ + name: 'my-default-header', + params: { + loader: () => + import('./MyDefaultHeader').then(m => ), + }, + }); + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/frontend-test-utils@0.3.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-react@0.4.31 + ## 1.16.0-next.1 ### Patch Changes @@ -78,7 +115,7 @@ The layout components receive card elements and can render them as they see fit. Cards is an array of objects with the following properties: - element: `JSx.Element`; - - type: `"peek" | "info" | "full" | undefined`; + - type: `"summary" | "info" | "content" | undefined`; ### Usage example @@ -115,14 +152,14 @@ {cards - .filter(card => card.type === 'peek') + .filter(card => card.type === 'summary') .map((card, index) => ( {card.element} ))} {cards - .filter(card => !card.type || card.type === 'full') + .filter(card => !card.type || card.type === 'content') .map((card, index) => ( {card.element} @@ -176,9 +213,9 @@ Initially the following three types are supported: - - `peek`: small vertical cards that provide information at a glance, for example recent builds, deployments, and service health. + - `summary`: small vertical cards that provide information at a glance, for example recent builds, deployments, and service health. - `info`: medium size cards with high priority and frequently used information such as common actions, entity metadata, and links. - - `full`: Large cards that are more feature rich with more information, typically used by plugins that don't quite need the full content view and want to show a card instead. + - `content`: Large cards that are more feature rich with more information, typically used by plugins that don't quite need the content content view and want to show a card instead. ### Usage examples diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 33893deefc..3809a18c09 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-react", - "version": "1.16.0-next.1", + "version": "1.16.0-next.2", "description": "A frontend library that helps other Backstage plugins interact with the catalog", "backstage": { "role": "web-library", diff --git a/plugins/catalog-react/report-alpha.api.md b/plugins/catalog-react/report-alpha.api.md index c191e55f2a..4a154356d1 100644 --- a/plugins/catalog-react/report-alpha.api.md +++ b/plugins/catalog-react/report-alpha.api.md @@ -137,11 +137,11 @@ export const EntityCardBlueprint: ExtensionBlueprint<{ inputs: {}; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; dataRefs: { filterFunction: ConfigurableExtensionDataRef< @@ -163,7 +163,7 @@ export const EntityCardBlueprint: ExtensionBlueprint<{ }>; // @alpha (undocumented) -export type EntityCardType = 'peek' | 'info' | 'full'; +export type EntityCardType = 'summary' | 'info' | 'content'; // @alpha export const EntityContentBlueprint: ExtensionBlueprint<{ diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx index f4e518799e..70c2b9d849 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx @@ -175,9 +175,9 @@ describe('EntityCardBlueprint', () => { }, "type": { "enum": [ - "peek", + "summary", "info", - "full", + "content", ], "type": "string", }, diff --git a/plugins/catalog-react/src/alpha/blueprints/extensionData.tsx b/plugins/catalog-react/src/alpha/blueprints/extensionData.tsx index bae7d4e668..c085912123 100644 --- a/plugins/catalog-react/src/alpha/blueprints/extensionData.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/extensionData.tsx @@ -54,13 +54,13 @@ export const entityContentGroupDataRef = createExtensionDataRef().with({ * Available entity card types */ export const entityCardTypes = [ - 'peek', + 'summary', 'info', - 'full', + 'content', ] as const satisfies readonly EntityCardType[]; /** @alpha */ -export type EntityCardType = 'peek' | 'info' | 'full'; +export type EntityCardType = 'summary' | 'info' | 'content'; /** @internal */ export const entityCardTypeDataRef = diff --git a/plugins/catalog-unprocessed-entities/CHANGELOG.md b/plugins/catalog-unprocessed-entities/CHANGELOG.md index 112c60b877..8eac4566bc 100644 --- a/plugins/catalog-unprocessed-entities/CHANGELOG.md +++ b/plugins/catalog-unprocessed-entities/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-unprocessed-entities +## 0.2.15-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + ## 0.2.15-next.1 ### Patch Changes diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index c18e957860..e3540de818 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.15-next.1", + "version": "0.2.15-next.2", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-unprocessed-entities", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 08b7c83c50..896249a08c 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,75 @@ # @backstage/plugin-catalog +## 1.28.0-next.2 + +### Minor Changes + +- 247a40b: Now a custom entity page header can be passed as input to the default entity page. +- 93533bd: The order in which group tabs appear on the entity page has been changed. + + ### Before + + Previously, entity contents determined the order in which groups were rendered, so a group was rendered as soon as its first entity content was detected. + + ### After + + Groups are now rendered first by default based on their order in the `app-config.yaml` file: + + ```diff + app: + extensions: + - page:catalog/entity: + + config: + + groups: + + # this will be the first tab of the default entity page + + - deployment: + + title: Deployment + + # this will be the second tab of the default entiy page + + - documentation: + + title: Documentation + ``` + + If you wish to place a normal tab before a group, you must add the tab to a group and place the group in the order you wish it to appear on the entity page (groups that contains only one tab are rendered as normal tabs). + + ```diff + app: + extensions: + - page:catalog/entity: + config: + groups: + + # Example placing the overview tab first + + - overview: + + title: Overview + - deployment: + title: Deployment + # this will be the second tab of the default entiy page + - documentation: + title: Documentation + - entity-content:catalog/overview: + + config: + + group: 'overview' + ``` + +### Patch Changes + +- 31731b0: Internal refactor to avoid `expiry-map` dependency. +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/plugin-search-react@1.8.7-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-react@0.4.31 + - @backstage/plugin-scaffolder-common@1.5.10-next.0 + - @backstage/plugin-search-common@1.2.17 + ## 1.28.0-next.1 ### Minor Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 2d12ec5367..67711d4a35 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "1.28.0-next.1", + "version": "1.28.0-next.2", "description": "The Backstage plugin for browsing the Backstage catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog/report-alpha.api.md b/plugins/catalog/report-alpha.api.md index ca6d01b0ac..77d8dbec50 100644 --- a/plugins/catalog/report-alpha.api.md +++ b/plugins/catalog/report-alpha.api.md @@ -354,11 +354,11 @@ const _default: FrontendPlugin< name: 'about'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef @@ -395,11 +395,11 @@ const _default: FrontendPlugin< name: 'depends-on-components'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef @@ -436,11 +436,11 @@ const _default: FrontendPlugin< name: 'depends-on-resources'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef @@ -477,11 +477,11 @@ const _default: FrontendPlugin< name: 'has-components'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef @@ -518,11 +518,11 @@ const _default: FrontendPlugin< name: 'has-resources'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef @@ -559,11 +559,11 @@ const _default: FrontendPlugin< name: 'has-subcomponents'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef @@ -600,11 +600,11 @@ const _default: FrontendPlugin< name: 'has-subdomains'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef @@ -641,11 +641,11 @@ const _default: FrontendPlugin< name: 'has-systems'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef @@ -682,11 +682,11 @@ const _default: FrontendPlugin< name: 'labels'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef @@ -723,11 +723,11 @@ const _default: FrontendPlugin< name: 'links'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef diff --git a/plugins/catalog/src/alpha/DefaultEntityContentLayout.tsx b/plugins/catalog/src/alpha/DefaultEntityContentLayout.tsx index 7a73f991bc..c5cbe452f2 100644 --- a/plugins/catalog/src/alpha/DefaultEntityContentLayout.tsx +++ b/plugins/catalog/src/alpha/DefaultEntityContentLayout.tsx @@ -35,14 +35,14 @@ import { HorizontalScrollGrid } from '@backstage/core-components'; const useStyles = makeStyles< Theme, - { infoCards: boolean; peekCards: boolean; fullCards: boolean } + { infoCards: boolean; summaryCards: boolean; contentCards: boolean } >(theme => ({ root: { display: 'flex', flexFlow: 'column nowrap', gap: theme.spacing(3), }, - fullArea: { + contentArea: { display: 'flex', flexFlow: 'column', gap: theme.spacing(3), @@ -56,10 +56,10 @@ const useStyles = makeStyles< gap: theme.spacing(3), minWidth: 0, }, - peekArea: { + summaryArea: { margin: theme.spacing(1.5), // To counteract MUI negative grid margin }, - peekCard: { + summaryCard: { flex: '0 0 auto', '& + &': { marginLeft: theme.spacing(3), @@ -69,9 +69,9 @@ const useStyles = makeStyles< root: { display: 'grid', gap: 0, - gridTemplateAreas: ({ peekCards }) => ` - "${peekCards ? 'peek' : 'full'} info" - "full info" + gridTemplateAreas: ({ summaryCards }) => ` + "${summaryCards ? 'summary' : 'content'} info" + "content info" `, gridTemplateColumns: ({ infoCards }) => (infoCards ? '2fr 1fr' : '1fr'), alignItems: 'start', @@ -82,11 +82,11 @@ const useStyles = makeStyles< top: theme.spacing(3), marginLeft: theme.spacing(3), }, - fullArea: { - gridArea: 'full', + contentArea: { + gridArea: 'content', }, - peekArea: { - gridArea: 'peek', + summaryArea: { + gridArea: 'summary', marginBottom: theme.spacing(3), }, }, @@ -124,13 +124,15 @@ export function DefaultEntityContentLayout(props: EntityContentLayoutProps) { const { cards } = props; const infoCards = cards.filter(card => card.type === 'info'); - const peekCards = cards.filter(card => card.type === 'peek'); - const fullCards = cards.filter(card => !card.type || card.type === 'full'); + const summaryCards = cards.filter(card => card.type === 'summary'); + const contentCards = cards.filter( + card => !card.type || card.type === 'content', + ); const classes = useStyles({ infoCards: !!infoCards.length, - peekCards: !!peekCards.length, - fullCards: !!fullCards.length, + summaryCards: !!summaryCards.length, + contentCards: !!contentCards.length, }); return ( @@ -142,18 +144,18 @@ export function DefaultEntityContentLayout(props: EntityContentLayoutProps) { {infoCards.map(card => card.element)}
) : null} - {peekCards.length > 0 ? ( -
+ {summaryCards.length > 0 ? ( +
- {peekCards.map(card => ( -
{card.element}
+ {summaryCards.map(card => ( +
{card.element}
))}
) : null} - {fullCards.length > 0 ? ( -
- {fullCards.map(card => card.element)} + {contentCards.length > 0 ? ( +
+ {contentCards.map(card => card.element)}
) : null}
diff --git a/plugins/config-schema/CHANGELOG.md b/plugins/config-schema/CHANGELOG.md index 43558f5f80..e1a8813f81 100644 --- a/plugins/config-schema/CHANGELOG.md +++ b/plugins/config-schema/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-config-schema +## 0.1.66-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.1.66-next.0 ### Patch Changes diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index 389c49f002..0d99cb3123 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.66-next.0", + "version": "0.1.66-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 b33f9bb723..66c2cf9b81 100644 --- a/plugins/devtools-backend/CHANGELOG.md +++ b/plugins/devtools-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-devtools-backend +## 0.5.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.0-next.0 + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-devtools-common@0.1.15 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-node@0.8.9-next.1 + ## 0.5.3-next.1 ### Patch Changes diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index 6e27f0042f..55f90afc28 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-backend", - "version": "0.5.3-next.1", + "version": "0.5.3-next.2", "backstage": { "role": "backend-plugin", "pluginId": "devtools", diff --git a/plugins/devtools/CHANGELOG.md b/plugins/devtools/CHANGELOG.md index 38c350f233..f2731bc555 100644 --- a/plugins/devtools/CHANGELOG.md +++ b/plugins/devtools/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-devtools +## 0.1.25-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/plugin-devtools-common@0.1.15 + - @backstage/plugin-permission-react@0.4.31 + ## 0.1.25-next.1 ### Patch Changes diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index 374559121c..3386cc4b36 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools", - "version": "0.1.25-next.1", + "version": "0.1.25-next.2", "backstage": { "role": "frontend-plugin", "pluginId": "devtools", diff --git a/plugins/events-backend-module-aws-sqs/CHANGELOG.md b/plugins/events-backend-module-aws-sqs/CHANGELOG.md index e09be1d750..12c383da36 100644 --- a/plugins/events-backend-module-aws-sqs/CHANGELOG.md +++ b/plugins/events-backend-module-aws-sqs/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-events-backend-module-aws-sqs +## 0.4.9-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + ## 0.4.9-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-aws-sqs/package.json b/plugins/events-backend-module-aws-sqs/package.json index 240a8e09c1..b415cfed2a 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.4.9-next.1", + "version": "0.4.9-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-azure/CHANGELOG.md b/plugins/events-backend-module-azure/CHANGELOG.md index a76d0b376b..7964007e85 100644 --- a/plugins/events-backend-module-azure/CHANGELOG.md +++ b/plugins/events-backend-module-azure/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-azure +## 0.2.18-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + ## 0.2.18-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-azure/package.json b/plugins/events-backend-module-azure/package.json index e0597bfc04..4faaec2894 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.18-next.1", + "version": "0.2.18-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md index 864917568d..1d48f9c126 100644 --- a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-bitbucket-cloud +## 0.2.18-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + ## 0.2.18-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-bitbucket-cloud/package.json b/plugins/events-backend-module-bitbucket-cloud/package.json index 47aaf4c4d6..3dee4b53df 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.18-next.1", + "version": "0.2.18-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-gerrit/CHANGELOG.md b/plugins/events-backend-module-gerrit/CHANGELOG.md index 4002632b7e..cfc38635fa 100644 --- a/plugins/events-backend-module-gerrit/CHANGELOG.md +++ b/plugins/events-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-gerrit +## 0.2.18-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + ## 0.2.18-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-gerrit/package.json b/plugins/events-backend-module-gerrit/package.json index 8474da935b..6d4a0b163c 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.18-next.1", + "version": "0.2.18-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-github/CHANGELOG.md b/plugins/events-backend-module-github/CHANGELOG.md index 4d04573ae3..ff479d22a1 100644 --- a/plugins/events-backend-module-github/CHANGELOG.md +++ b/plugins/events-backend-module-github/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-github +## 0.2.18-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + ## 0.2.18-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-github/package.json b/plugins/events-backend-module-github/package.json index bee5b66f05..f2d171bb42 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.18-next.1", + "version": "0.2.18-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-gitlab/CHANGELOG.md b/plugins/events-backend-module-gitlab/CHANGELOG.md index aa1e4aae9b..02c3f7ea71 100644 --- a/plugins/events-backend-module-gitlab/CHANGELOG.md +++ b/plugins/events-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-gitlab +## 0.2.18-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + ## 0.2.18-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-gitlab/package.json b/plugins/events-backend-module-gitlab/package.json index db978f4ea5..ba1b090769 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.18-next.1", + "version": "0.2.18-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-test-utils/CHANGELOG.md b/plugins/events-backend-test-utils/CHANGELOG.md index f15325e3df..f04f62f673 100644 --- a/plugins/events-backend-test-utils/CHANGELOG.md +++ b/plugins/events-backend-test-utils/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-events-backend-test-utils +## 0.1.42-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + ## 0.1.42-next.1 ### Patch Changes diff --git a/plugins/events-backend-test-utils/package.json b/plugins/events-backend-test-utils/package.json index 748b75c5c7..88185e9745 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.42-next.1", + "version": "0.1.42-next.2", "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 2eb7ac5419..fa58ec66d9 100644 --- a/plugins/events-backend/CHANGELOG.md +++ b/plugins/events-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-events-backend +## 0.4.4-next.2 + +### Patch Changes + +- b95aa77: add `addHttpPostBodyParser` to events extension to allow body parse customization. This feature will enhance flexibility in handling HTTP POST requests in event-related operations. +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-openapi-utils@0.5.1-next.1 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.4.4-next.1 ### Patch Changes diff --git a/plugins/events-backend/README.md b/plugins/events-backend/README.md index 7f88452ff6..219b60f213 100644 --- a/plugins/events-backend/README.md +++ b/plugins/events-backend/README.md @@ -140,6 +140,48 @@ export const eventsModuleYourFeature = createBackendModule({ }); ``` +### Request Body Parse + +We need to parse the request body before we can validate it. We have some default parsers but you can provide your own when necessary. + +```ts +import { eventsExtensionPoint } from '@backstage/plugin-events-node/alpha'; + +// [...] + +export const eventsModuleYourFeature = createBackendModule({ + pluginId: 'events', + moduleId: 'your-feature', + register(env) { + // [...] + env.registerInit({ + deps: { + // [...] + events: eventsExtensionPoint, + // [...] + }, + async init({ /* ... */ events /*, ... */ }) { + // [...] + events.addHttpPostBodyParser({ + contentType: 'application/x-www-form-urlencoded', + parser: async (req, _topic) => { + return { + bodyParsed: req.body.toString('utf-8'), + bodyBuffer: req.body, + encoding: 'utf-8', + }; + }, + }); + }, + }); + }, +}); +``` + +We have the following default parsers: + +- `application/json` + #### Legacy Backend System ```ts diff --git a/plugins/events-backend/package.json b/plugins/events-backend/package.json index 6d7690d9dc..44acf9b557 100644 --- a/plugins/events-backend/package.json +++ b/plugins/events-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend", - "version": "0.4.4-next.1", + "version": "0.4.4-next.2", "backstage": { "role": "backend-plugin", "pluginId": "events", diff --git a/plugins/events-backend/report.api.md b/plugins/events-backend/report.api.md index fb9f5fb237..286bc8317d 100644 --- a/plugins/events-backend/report.api.md +++ b/plugins/events-backend/report.api.md @@ -11,6 +11,7 @@ import { EventPublisher } from '@backstage/plugin-events-node'; import { EventsService } from '@backstage/plugin-events-node'; import { EventSubscriber } from '@backstage/plugin-events-node'; import express from 'express'; +import { HttpBodyParser } from '@backstage/plugin-events-node'; import { HttpPostIngressOptions } from '@backstage/plugin-events-node'; import { Logger } from 'winston'; import { LoggerService } from '@backstage/backend-plugin-api'; @@ -58,6 +59,9 @@ export class HttpPostIngressEventPublisher { ingresses?: { [topic: string]: Omit; }; + bodyParsers?: { + [contentType: string]: HttpBodyParser; + }; logger: LoggerService; }): HttpPostIngressEventPublisher; } diff --git a/plugins/events-backend/src/service/EventsPlugin.test.ts b/plugins/events-backend/src/service/EventsPlugin.test.ts index 35813d7b8e..12a0f63002 100644 --- a/plugins/events-backend/src/service/EventsPlugin.test.ts +++ b/plugins/events-backend/src/service/EventsPlugin.test.ts @@ -141,7 +141,7 @@ describe('eventsPlugin', () => { } const databases = TestDatabases.create({ - ids: ['SQLITE_3', 'MYSQL_8', 'POSTGRES_9', 'POSTGRES_13', 'POSTGRES_16'], + ids: ['SQLITE_3', 'MYSQL_8', 'POSTGRES_13', 'POSTGRES_17'], }); async function mockKnexFactory(databaseId: TestDatabaseId) { diff --git a/plugins/events-backend/src/service/EventsPlugin.ts b/plugins/events-backend/src/service/EventsPlugin.ts index a837e69c32..1b43f0c527 100644 --- a/plugins/events-backend/src/service/EventsPlugin.ts +++ b/plugins/events-backend/src/service/EventsPlugin.ts @@ -24,6 +24,7 @@ import { } from '@backstage/plugin-events-node/alpha'; import { eventsServiceRef, + HttpBodyParserOptions, HttpPostIngressOptions, } from '@backstage/plugin-events-node'; import Router from 'express-promise-router'; @@ -31,7 +32,8 @@ import { HttpPostIngressEventPublisher } from './http'; import { createEventBusRouter } from './hub'; class EventsExtensionPointImpl implements EventsExtensionPoint { - #httpPostIngresses: HttpPostIngressOptions[] = []; + readonly #httpPostIngresses: HttpPostIngressOptions[] = []; + readonly #httpBodyParsers: HttpBodyParserOptions[] = []; setEventBroker(_: any): void { throw new Error( @@ -55,9 +57,17 @@ class EventsExtensionPointImpl implements EventsExtensionPoint { this.#httpPostIngresses.push(options); } + addHttpPostBodyParser(options: HttpBodyParserOptions): void { + this.#httpBodyParsers.push(options); + } + get httpPostIngresses() { return this.#httpPostIngresses; } + + get httpBodyParsers() { + return this.#httpBodyParsers; + } } /** @@ -99,10 +109,18 @@ export const eventsPlugin = createBackendPlugin({ ]), ); + const bodyParsers = Object.fromEntries( + extensionPoint.httpBodyParsers.map(option => [ + option.contentType, + option.parser, + ]), + ); + const http = HttpPostIngressEventPublisher.fromConfig({ config, events, ingresses, + bodyParsers, logger, }); const eventsRouter = Router(); diff --git a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts index 45ee055fa9..23e7a4212d 100644 --- a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts +++ b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts @@ -22,6 +22,7 @@ import request from 'supertest'; import { HttpPostIngressEventPublisher } from './HttpPostIngressEventPublisher'; import { mockServices } from '@backstage/backend-test-utils'; import { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter'; +import { HttpBodyParser } from '@backstage/plugin-events-node'; const middleware = MiddlewareFactory.create({ logger: mockServices.logger.mock(), @@ -287,7 +288,8 @@ describe('HttpPostIngressEventPublisher', () => { expect(response.body).toEqual( expect.objectContaining({ error: { - message: 'Unsupported media type: text/plain', + message: + 'Unsupported media type: text/plain. You need to provide a custom body parser for this media type using the EventsExtensionPoint.', name: 'UnsupportedMediaTypeError', statusCode: 415, }, @@ -297,6 +299,134 @@ describe('HttpPostIngressEventPublisher', () => { ); }); + it('with a text/plain body parser implementation', async () => { + const config = new ConfigReader({ + events: { + http: { + topics: ['testA'], + }, + }, + }); + + const router = Router(); + const app = express().use(router); + const events = new TestEventsService(); + + const bodyParser: HttpBodyParser = async (req, _topic) => { + return { + bodyParsed: req.body.toString('utf-8'), + bodyBuffer: req.body, + encoding: 'utf-8', + }; + }; + + const publisher = HttpPostIngressEventPublisher.fromConfig({ + config, + events, + bodyParsers: { + 'text/plain': bodyParser, + }, + logger, + }); + publisher.bind(router); + router.use(middleware.error()); + + const response = await request(app) + .post('/http/testA') + .type('text/plain') + .timeout(1000) + .send('Textual information'); + expect(response.status).toBe(202); + }); + + it('with a invalid media type', async () => { + const config = new ConfigReader({ + events: { + http: { + topics: ['testA'], + }, + }, + }); + + const router = Router(); + const app = express().use(router); + const events = new TestEventsService(); + + const publisher = HttpPostIngressEventPublisher.fromConfig({ + config, + events, + logger, + }); + publisher.bind(router); + router.use(middleware.error()); + + const response = await request(app) + .post('/http/testA') + .type('not-valid-content/plain') + .timeout(1000) + .send('Textual information'); + expect(response.status).toBe(415); + expect(response.body).toEqual( + expect.objectContaining({ + error: { + message: + 'Unsupported media type: not-valid-content/plain. You need to provide a custom body parser for this media type using the EventsExtensionPoint.', + name: 'UnsupportedMediaTypeError', + statusCode: 415, + }, + request: { method: 'POST', url: '/http/testA' }, + response: { statusCode: 415 }, + }), + ); + }); + + it('with a custom application/json body parser implementation', async () => { + const config = new ConfigReader({ + events: { + http: { + topics: ['testA'], + }, + }, + }); + + const router = Router(); + const app = express().use(router); + const events = new TestEventsService(); + const customParse = jest.fn(); + + const bodyParser: HttpBodyParser = async ( + req, + _parsedMediaType, + _topic, + ) => { + customParse(); + return { + bodyParsed: JSON.parse(req.body.toString('utf-8')), + bodyBuffer: req.body, + encoding: 'utf-8', + }; + }; + + const publisher = HttpPostIngressEventPublisher.fromConfig({ + config, + events, + bodyParsers: { + 'application/json': bodyParser, + }, + logger, + }); + publisher.bind(router); + router.use(middleware.error()); + + const response = await request(app) + .post('/http/testA') + .type('application/json') + .timeout(1000) + .send(JSON.stringify({ testA: 'data' })); + expect(response.status).toBe(202); + expect(customParse).toHaveBeenCalled(); + }); + it('with validator', async () => { const config = new ConfigReader({ events: { diff --git a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts index a14bb7243e..8ac6542e81 100644 --- a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts +++ b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts @@ -16,35 +16,18 @@ import { LoggerService } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; -import { CustomErrorBase } from '@backstage/errors'; import { EventsService, + HttpBodyParser, HttpPostIngressOptions, RequestValidator, } from '@backstage/plugin-events-node'; import contentType from 'content-type'; import express from 'express'; import Router from 'express-promise-router'; +import { defaultHttpBodyParsers } from './body-parser'; import { RequestValidationContextImpl } from './validation'; - -class UnsupportedCharsetError extends CustomErrorBase { - name = 'UnsupportedCharsetError' as const; - statusCode = 415 as const; - - constructor(charset: string) { - super(`Unsupported charset: ${charset}`); - } -} - -class UnsupportedMediaTypeError extends CustomErrorBase { - name = 'UnsupportedMediaTypeError' as const; - statusCode = 415 as const; - - constructor(mediaType?: string) { - super(`Unsupported media type: ${mediaType ?? 'unknown'}`); - } -} - +import { UnsupportedMediaTypeError } from './errors'; /** * Publishes events received from their origin (e.g., webhook events from an SCM system) * via HTTP POST endpoint and passes the request body as event payload to the registered subscribers. @@ -57,6 +40,7 @@ export class HttpPostIngressEventPublisher { config: Config; events: EventsService; ingresses?: { [topic: string]: Omit }; + bodyParsers?: { [contentType: string]: HttpBodyParser }; logger: LoggerService; }): HttpPostIngressEventPublisher { const topics = @@ -71,7 +55,14 @@ export class HttpPostIngressEventPublisher { } }); - return new HttpPostIngressEventPublisher(env.events, env.logger, ingresses); + const parsers = { ...defaultHttpBodyParsers, ...env.bodyParsers }; + + return new HttpPostIngressEventPublisher( + env.events, + env.logger, + ingresses, + parsers, + ); } private constructor( @@ -80,6 +71,9 @@ export class HttpPostIngressEventPublisher { private readonly ingresses: { [topic: string]: Omit; }, + private readonly bodyParsers: { + [contentType: string]: HttpBodyParser; + }, ) {} bind(router: express.Router): void { @@ -108,32 +102,18 @@ export class HttpPostIngressEventPublisher { const logger = this.logger; router.post(path, async (request, response) => { - const requestBody = request.body; - if (!Buffer.isBuffer(requestBody)) { - throw new Error( - `Failed to retrieve raw body from incoming event for topic ${topic}; not a buffer: ${typeof requestBody}`, - ); + const requestContentType = contentType.parse(request); + const bodyParser = this.bodyParsers[requestContentType.type ?? '']; + + if (!bodyParser) { + throw new UnsupportedMediaTypeError(requestContentType.type); } - const bodyBuffer: Buffer = requestBody; - const parsedContentType = contentType.parse(request); - if ( - !parsedContentType.type || - parsedContentType.type !== 'application/json' - ) { - throw new UnsupportedMediaTypeError(parsedContentType.type); - } - - const encoding = parsedContentType.parameters.charset ?? 'utf-8'; - if (!Buffer.isEncoding(encoding)) { - throw new UnsupportedCharsetError(encoding); - } - - const bodyString = bodyBuffer.toString(encoding); - const bodyParsed = - parsedContentType.type === 'application/json' - ? JSON.parse(bodyString) - : bodyString; + const { bodyParsed, bodyBuffer, encoding } = await bodyParser( + request, + requestContentType, + topic, + ); if (validator) { const requestDetails = { diff --git a/plugins/events-backend/src/service/http/body-parser/HttpApplicationJsonBodyParser.ts b/plugins/events-backend/src/service/http/body-parser/HttpApplicationJsonBodyParser.ts new file mode 100644 index 0000000000..fa716a5db0 --- /dev/null +++ b/plugins/events-backend/src/service/http/body-parser/HttpApplicationJsonBodyParser.ts @@ -0,0 +1,44 @@ +/* + * 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 { HttpBodyParser } from '@backstage/plugin-events-node'; +import { UnsupportedCharsetError } from '../errors'; + +export const HttpApplicationJsonBodyParser: HttpBodyParser = async ( + request, + parsedMediaType, + topic, +) => { + const requestBody = request.body; + if (!Buffer.isBuffer(requestBody)) { + throw new Error( + `Failed to retrieve raw body from incoming event for topic ${topic}; not a buffer: ${typeof requestBody}`, + ); + } + + const bodyBuffer: Buffer = requestBody; + + const encoding = parsedMediaType.parameters.charset ?? 'utf-8'; + if (!Buffer.isEncoding(encoding)) { + throw new UnsupportedCharsetError(encoding); + } + + const bodyString = bodyBuffer.toString(encoding); + const bodyParsed = + parsedMediaType.type === 'application/json' + ? JSON.parse(bodyString) + : bodyString; + return { bodyParsed, bodyBuffer, encoding }; +}; diff --git a/plugins/events-backend/src/service/http/body-parser/index.ts b/plugins/events-backend/src/service/http/body-parser/index.ts new file mode 100644 index 0000000000..7fc17ef235 --- /dev/null +++ b/plugins/events-backend/src/service/http/body-parser/index.ts @@ -0,0 +1,22 @@ +/* + * 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 { HttpBodyParser } from '@backstage/plugin-events-node'; +import { HttpApplicationJsonBodyParser } from './HttpApplicationJsonBodyParser'; + +export const defaultHttpBodyParsers: { [contentType: string]: HttpBodyParser } = + { + 'application/json': HttpApplicationJsonBodyParser, + }; diff --git a/plugins/events-backend/src/service/http/errors.ts b/plugins/events-backend/src/service/http/errors.ts new file mode 100644 index 0000000000..583f1a6b9b --- /dev/null +++ b/plugins/events-backend/src/service/http/errors.ts @@ -0,0 +1,38 @@ +/* + * 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 { CustomErrorBase } from '@backstage/errors'; + +export class UnsupportedCharsetError extends CustomErrorBase { + name = 'UnsupportedCharsetError' as const; + statusCode = 415 as const; + + constructor(charset: string) { + super(`Unsupported charset: ${charset}`); + } +} + +export class UnsupportedMediaTypeError extends CustomErrorBase { + name = 'UnsupportedMediaTypeError' as const; + statusCode = 415 as const; + + constructor(mediaType?: string) { + super( + `Unsupported media type: ${ + mediaType ?? 'unknown' + }. You need to provide a custom body parser for this media type using the EventsExtensionPoint.`, + ); + } +} diff --git a/plugins/events-backend/src/service/hub/DatabaseEventBusStore.test.ts b/plugins/events-backend/src/service/hub/DatabaseEventBusStore.test.ts index ade462a920..959341cbc0 100644 --- a/plugins/events-backend/src/service/hub/DatabaseEventBusStore.test.ts +++ b/plugins/events-backend/src/service/hub/DatabaseEventBusStore.test.ts @@ -24,7 +24,7 @@ import { DatabaseEventBusStore } from './DatabaseEventBusStore'; const logger = mockServices.logger.mock(); const databases = TestDatabases.create({ - ids: ['POSTGRES_9', 'POSTGRES_13', 'POSTGRES_16'], + ids: ['POSTGRES_13', 'POSTGRES_17'], }); const maybeDescribe = diff --git a/plugins/events-node/CHANGELOG.md b/plugins/events-node/CHANGELOG.md index 9261dca0c4..f7322fa635 100644 --- a/plugins/events-node/CHANGELOG.md +++ b/plugins/events-node/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-events-node +## 0.4.9-next.2 + +### Patch Changes + +- b95aa77: add `addHttpPostBodyParser` to events extension to allow body parse customization. This feature will enhance flexibility in handling HTTP POST requests in event-related operations. +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.4.9-next.1 ### Patch Changes diff --git a/plugins/events-node/package.json b/plugins/events-node/package.json index 5c8ba8223d..d0fbb4917a 100644 --- a/plugins/events-node/package.json +++ b/plugins/events-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-node", - "version": "0.4.9-next.1", + "version": "0.4.9-next.2", "description": "The plugin-events-node module for @backstage/plugin-events-backend", "backstage": { "role": "node-library", @@ -55,7 +55,11 @@ "@backstage/backend-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/types": "workspace:^", + "@types/content-type": "^1.1.8", + "@types/express": "^4.17.6", + "content-type": "^1.0.5", "cross-fetch": "^4.0.0", + "express": "^4.17.1", "uri-template": "^2.0.0" }, "devDependencies": { diff --git a/plugins/events-node/report-alpha.api.md b/plugins/events-node/report-alpha.api.md index f61048ac94..ba8b7e9adf 100644 --- a/plugins/events-node/report-alpha.api.md +++ b/plugins/events-node/report-alpha.api.md @@ -7,10 +7,13 @@ import { EventBroker } from '@backstage/plugin-events-node'; import { EventPublisher } from '@backstage/plugin-events-node'; import { EventSubscriber } from '@backstage/plugin-events-node'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; +import { HttpBodyParserOptions } from '@backstage/plugin-events-node'; import { HttpPostIngressOptions } from '@backstage/plugin-events-node'; // @alpha (undocumented) export interface EventsExtensionPoint { + // (undocumented) + addHttpPostBodyParser(options: HttpBodyParserOptions): void; // (undocumented) addHttpPostIngress(options: HttpPostIngressOptions): void; // @deprecated (undocumented) diff --git a/plugins/events-node/report.api.md b/plugins/events-node/report.api.md index 8bc3145dd5..ada3e2dc1a 100644 --- a/plugins/events-node/report.api.md +++ b/plugins/events-node/report.api.md @@ -7,6 +7,8 @@ import { AuthService } from '@backstage/backend-plugin-api'; import { DiscoveryService } from '@backstage/backend-plugin-api'; import { LifecycleService } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; +import { ParsedMediaType } from 'content-type'; +import { Request as Request_2 } from 'express'; import { RootConfigService } from '@backstage/backend-plugin-api'; import { ServiceFactory } from '@backstage/backend-plugin-api'; import { ServiceRef } from '@backstage/backend-plugin-api'; @@ -109,6 +111,28 @@ export interface EventSubscriber { supportsEventTopics(): string[]; } +// @public (undocumented) +export type HttpBodyParsed = { + bodyParsed: any; + bodyBuffer: Buffer; + encoding: string; +}; + +// @public (undocumented) +export type HttpBodyParser = ( + request: Request_2, + parsedMediaType: ParsedMediaType, + topic: string, +) => Promise; + +// @public (undocumented) +export interface HttpBodyParserOptions { + // (undocumented) + contentType: string; + // (undocumented) + parser: HttpBodyParser; +} + // @public (undocumented) export interface HttpPostIngressOptions { // (undocumented) diff --git a/plugins/events-node/src/api/http/HttpBodyParserOptions.ts b/plugins/events-node/src/api/http/HttpBodyParserOptions.ts new file mode 100644 index 0000000000..cedaecf28e --- /dev/null +++ b/plugins/events-node/src/api/http/HttpBodyParserOptions.ts @@ -0,0 +1,24 @@ +/* + * 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 { HttpBodyParser } from './body-parser'; + +/** + * @public + */ +export interface HttpBodyParserOptions { + contentType: string; + parser: HttpBodyParser; +} diff --git a/plugins/events-node/src/api/http/body-parser/HttpBodyParser.ts b/plugins/events-node/src/api/http/body-parser/HttpBodyParser.ts new file mode 100644 index 0000000000..c0b6a46b95 --- /dev/null +++ b/plugins/events-node/src/api/http/body-parser/HttpBodyParser.ts @@ -0,0 +1,35 @@ +/* + * 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 { Request } from 'express'; +import { ParsedMediaType } from 'content-type'; +/** + * @public + */ +export type HttpBodyParsed = { + bodyParsed: any; + bodyBuffer: Buffer; + encoding: string; +}; + +/** + * @public + */ +export type HttpBodyParser = ( + request: Request, + parsedMediaType: ParsedMediaType, + topic: string, +) => Promise; diff --git a/plugins/events-node/src/api/http/body-parser/index.ts b/plugins/events-node/src/api/http/body-parser/index.ts new file mode 100644 index 0000000000..e1376841ae --- /dev/null +++ b/plugins/events-node/src/api/http/body-parser/index.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export * from './HttpBodyParser'; diff --git a/plugins/events-node/src/api/http/index.ts b/plugins/events-node/src/api/http/index.ts index 9206819a4a..c0908583ad 100644 --- a/plugins/events-node/src/api/http/index.ts +++ b/plugins/events-node/src/api/http/index.ts @@ -15,4 +15,6 @@ */ export type { HttpPostIngressOptions } from './HttpPostIngressOptions'; +export type { HttpBodyParserOptions } from './HttpBodyParserOptions'; export * from './validation'; +export * from './body-parser'; diff --git a/plugins/events-node/src/extensions.ts b/plugins/events-node/src/extensions.ts index 90add52563..72be2db51f 100644 --- a/plugins/events-node/src/extensions.ts +++ b/plugins/events-node/src/extensions.ts @@ -19,6 +19,7 @@ import { EventBroker, EventPublisher, EventSubscriber, + HttpBodyParserOptions, HttpPostIngressOptions, } from '@backstage/plugin-events-node'; @@ -46,6 +47,8 @@ export interface EventsExtensionPoint { ): void; addHttpPostIngress(options: HttpPostIngressOptions): void; + + addHttpPostBodyParser(options: HttpBodyParserOptions): void; } /** diff --git a/plugins/example-todo-list/CHANGELOG.md b/plugins/example-todo-list/CHANGELOG.md index a3918aa963..0d6e6ff09d 100644 --- a/plugins/example-todo-list/CHANGELOG.md +++ b/plugins/example-todo-list/CHANGELOG.md @@ -1,5 +1,13 @@ # @internal/plugin-todo-list +## 1.0.37-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + ## 1.0.37-next.0 ### Patch Changes diff --git a/plugins/example-todo-list/package.json b/plugins/example-todo-list/package.json index 16787f1a41..b9fc557436 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.37-next.0", + "version": "1.0.37-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "todo-list", diff --git a/plugins/home-react/CHANGELOG.md b/plugins/home-react/CHANGELOG.md index 4f05840812..8e4b6cebf8 100644 --- a/plugins/home-react/CHANGELOG.md +++ b/plugins/home-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-home-react +## 0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + ## 0.1.24-next.0 ### Patch Changes diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index cb9cb2c3a8..0972d84ee5 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.24-next.0", + "version": "0.1.24-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 385743a6a7..d7c3f97a91 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-home +## 0.8.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-app-api@1.16.0-next.0 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/theme@0.6.4 + - @backstage/plugin-home-react@0.1.24-next.1 + ## 0.8.6-next.1 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index 3a77281f3c..4d0c8ec410 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home", - "version": "0.8.6-next.1", + "version": "0.8.6-next.2", "description": "A Backstage plugin that helps you build a home page", "backstage": { "role": "frontend-plugin", diff --git a/plugins/home/src/homePageComponents/StarredEntities/Content.test.tsx b/plugins/home/src/homePageComponents/StarredEntities/Content.test.tsx index ad26482961..b2522cda96 100644 --- a/plugins/home/src/homePageComponents/StarredEntities/Content.test.tsx +++ b/plugins/home/src/homePageComponents/StarredEntities/Content.test.tsx @@ -50,7 +50,24 @@ describe('StarredEntitiesContent', () => { mockedApi.toggleStarred('component:default/mock-starred-entity-2'); mockedApi.toggleStarred('component:default/mock-starred-entity-3'); - const mockCatalogApi = catalogApiMock({ entities }); + const mockCatalogApi = catalogApiMock.mock({ + getEntitiesByRefs: jest.fn().mockImplementation(async ({ fields }) => { + const expectedFields = [ + 'kind', + 'metadata.namespace', + 'metadata.name', + 'spec.type', + 'metadata.title', + 'spec.profile.displayName', + ]; + expectedFields.forEach(field => { + expect(fields).toContain(field); + }); + return { + items: entities, + }; + }), + }); const { getByText, queryByText } = await renderInTestApp( !!e); }, [catalogApi, starredEntities]); diff --git a/plugins/kubernetes-cluster/CHANGELOG.md b/plugins/kubernetes-cluster/CHANGELOG.md index 89b1504307..2a86d12499 100644 --- a/plugins/kubernetes-cluster/CHANGELOG.md +++ b/plugins/kubernetes-cluster/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-kubernetes-cluster +## 0.0.23-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/plugin-kubernetes-common@0.9.4-next.0 + - @backstage/plugin-kubernetes-react@0.5.5-next.2 + - @backstage/plugin-permission-react@0.4.31 + ## 0.0.23-next.1 ### Patch Changes diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index 1456b9ca46..3ca7dcd445 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.23-next.1", + "version": "0.0.23-next.2", "description": "A Backstage plugin that shows details of Kubernetes clusters", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes-react/CHANGELOG.md b/plugins/kubernetes-react/CHANGELOG.md index d4b78369b7..8dccce8ce2 100644 --- a/plugins/kubernetes-react/CHANGELOG.md +++ b/plugins/kubernetes-react/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kubernetes-react +## 0.5.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-kubernetes-common@0.9.4-next.0 + ## 0.5.5-next.1 ### Patch Changes diff --git a/plugins/kubernetes-react/package.json b/plugins/kubernetes-react/package.json index 4261b27e2c..42eefd8abc 100644 --- a/plugins/kubernetes-react/package.json +++ b/plugins/kubernetes-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-react", - "version": "0.5.5-next.1", + "version": "0.5.5-next.2", "description": "Web library for the kubernetes-react plugin", "backstage": { "role": "web-library", diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index bed3f31283..ee13cb7f65 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-kubernetes +## 0.12.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/plugin-kubernetes-common@0.9.4-next.0 + - @backstage/plugin-kubernetes-react@0.5.5-next.2 + - @backstage/plugin-permission-react@0.4.31 + ## 0.12.5-next.1 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 0cc5c777bf..379e65b66f 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.12.5-next.1", + "version": "0.12.5-next.2", "description": "A Backstage plugin that integrates towards Kubernetes", "backstage": { "role": "frontend-plugin", diff --git a/plugins/notifications-backend-module-email/CHANGELOG.md b/plugins/notifications-backend-module-email/CHANGELOG.md index abbd5b59cd..39a8dda7b4 100644 --- a/plugins/notifications-backend-module-email/CHANGELOG.md +++ b/plugins/notifications-backend-module-email/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-notifications-backend-module-email +## 0.3.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.13-next.2 + ## 0.3.7-next.1 ### Patch Changes diff --git a/plugins/notifications-backend-module-email/package.json b/plugins/notifications-backend-module-email/package.json index c4d15d3391..ff486b7e3d 100644 --- a/plugins/notifications-backend-module-email/package.json +++ b/plugins/notifications-backend-module-email/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend-module-email", - "version": "0.3.7-next.1", + "version": "0.3.7-next.2", "description": "The email backend module for the notifications plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/notifications-backend/CHANGELOG.md b/plugins/notifications-backend/CHANGELOG.md index 47dcd6bf2e..94f8ee65e9 100644 --- a/plugins/notifications-backend/CHANGELOG.md +++ b/plugins/notifications-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-notifications-backend +## 0.5.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.13-next.2 + - @backstage/plugin-signals-node@0.1.18-next.2 + ## 0.5.4-next.1 ### Patch Changes diff --git a/plugins/notifications-backend/package.json b/plugins/notifications-backend/package.json index c96539caf8..1cbd11a933 100644 --- a/plugins/notifications-backend/package.json +++ b/plugins/notifications-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend", - "version": "0.5.4-next.1", + "version": "0.5.4-next.2", "backstage": { "role": "backend-plugin", "pluginId": "notifications", diff --git a/plugins/notifications-node/CHANGELOG.md b/plugins/notifications-node/CHANGELOG.md index b4a20fe9c3..2291723d45 100644 --- a/plugins/notifications-node/CHANGELOG.md +++ b/plugins/notifications-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-notifications-node +## 0.2.13-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-signals-node@0.1.18-next.2 + ## 0.2.13-next.1 ### Patch Changes diff --git a/plugins/notifications-node/package.json b/plugins/notifications-node/package.json index e4703e2e07..8277b03975 100644 --- a/plugins/notifications-node/package.json +++ b/plugins/notifications-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-node", - "version": "0.2.13-next.1", + "version": "0.2.13-next.2", "description": "Node.js library for the notifications plugin", "backstage": { "role": "node-library", diff --git a/plugins/notifications/CHANGELOG.md b/plugins/notifications/CHANGELOG.md index f07540616e..04d83f31f3 100644 --- a/plugins/notifications/CHANGELOG.md +++ b/plugins/notifications/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-notifications +## 0.5.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-signals-react@0.0.10 + ## 0.5.3-next.1 ### Patch Changes diff --git a/plugins/notifications/package.json b/plugins/notifications/package.json index 6ff5b7b767..76a769db2c 100644 --- a/plugins/notifications/package.json +++ b/plugins/notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications", - "version": "0.5.3-next.1", + "version": "0.5.3-next.2", "backstage": { "role": "frontend-plugin", "pluginId": "notifications", diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index 2b598e177e..34018e412b 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-org-react +## 0.1.36-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + ## 0.1.36-next.1 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index 675967452a..170ce814ac 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.36-next.1", + "version": "0.1.36-next.2", "backstage": { "role": "web-library", "pluginId": "org", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index f11beec465..e8cbeb48b9 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-org +## 0.6.37-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/plugin-catalog-common@1.1.3 + ## 0.6.37-next.1 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index 37322b36ec..7a93b931f0 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.6.37-next.1", + "version": "0.6.37-next.2", "description": "A Backstage plugin that helps you create entity pages for your organization", "backstage": { "role": "frontend-plugin", diff --git a/plugins/org/report-alpha.api.md b/plugins/org/report-alpha.api.md index f85e7090dd..f6a79a968a 100644 --- a/plugins/org/report-alpha.api.md +++ b/plugins/org/report-alpha.api.md @@ -24,11 +24,11 @@ const _default: FrontendPlugin< name: 'group-profile'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef< @@ -69,11 +69,11 @@ const _default: FrontendPlugin< name: 'members-list'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef< @@ -114,11 +114,11 @@ const _default: FrontendPlugin< name: 'ownership'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef< @@ -159,11 +159,11 @@ const _default: FrontendPlugin< name: 'user-profile'; config: { filter: EntityPredicate | undefined; - type: 'full' | 'info' | 'peek' | undefined; + type: 'content' | 'summary' | 'info' | undefined; }; configInput: { filter?: EntityPredicate | undefined; - type?: 'full' | 'info' | 'peek' | undefined; + type?: 'content' | 'summary' | 'info' | undefined; }; output: | ConfigurableExtensionDataRef< diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index ee255293a8..7bd280b4fd 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -56,7 +56,6 @@ "test": "backstage-cli package test" }, "dependencies": { - "@backstage/backend-common": "^0.25.0", "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", diff --git a/plugins/permission-node/report.api.md b/plugins/permission-node/report.api.md index 71298a7d81..fca13ce5f0 100644 --- a/plugins/permission-node/report.api.md +++ b/plugins/permission-node/report.api.md @@ -29,7 +29,6 @@ import { PermissionsServiceRequestOptions } from '@backstage/backend-plugin-api' import { PolicyDecision } from '@backstage/plugin-permission-common'; import { QueryPermissionRequest } from '@backstage/plugin-permission-common'; import { ResourcePermission } from '@backstage/plugin-permission-common'; -import { TokenManager } from '@backstage/backend-common'; import { z } from 'zod'; // @public @@ -385,8 +384,7 @@ export class ServerPermissionClient implements PermissionsService { config: Config, options: { discovery: DiscoveryService; - tokenManager?: TokenManager; - auth?: AuthService; + auth: AuthService; }, ): ServerPermissionClient; } diff --git a/plugins/permission-node/src/ServerPermissionClient.ts b/plugins/permission-node/src/ServerPermissionClient.ts index c34a8fe930..fc9f554955 100644 --- a/plugins/permission-node/src/ServerPermissionClient.ts +++ b/plugins/permission-node/src/ServerPermissionClient.ts @@ -14,10 +14,6 @@ * limitations under the License. */ -import { - TokenManager, - createLegacyAuthAdapters, -} from '@backstage/backend-common'; import { AuthService, BackstageCredentials, @@ -53,28 +49,14 @@ export class ServerPermissionClient implements PermissionsService { config: Config, options: { discovery: DiscoveryService; - /** @deprecated This option will be removed in the future, provide a the auth option instead */ - tokenManager?: TokenManager; - auth?: AuthService; + auth: AuthService; }, ) { - const { discovery, tokenManager } = options; + const { auth, discovery } = options; const permissionClient = new PermissionClient({ discovery, config }); const permissionEnabled = config.getOptionalBoolean('permission.enabled') ?? false; - if ( - permissionEnabled && - tokenManager && - (tokenManager as any).isInsecureServerTokenManager - ) { - throw new Error( - 'Service-to-service authentication must be configured before enabling permissions. Read more here https://backstage.io/docs/auth/service-to-service-auth', - ); - } - - const { auth } = createLegacyAuthAdapters(options); - return new ServerPermissionClient({ auth, permissionClient, diff --git a/plugins/scaffolder-backend-module-azure/CHANGELOG.md b/plugins/scaffolder-backend-module-azure/CHANGELOG.md index 3bcee9d120..7374517b1a 100644 --- a/plugins/scaffolder-backend-module-azure/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-azure/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-azure +## 0.2.7-next.2 + +### Patch Changes + +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.2.7-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-azure/package.json b/plugins/scaffolder-backend-module-azure/package.json index 2fed1560f5..b08a1677a4 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.2.7-next.1", + "version": "0.2.7-next.2", "description": "The azure module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-azure/report.api.md b/plugins/scaffolder-backend-module-azure/report.api.md index e15aba0fa4..159bf02a00 100644 --- a/plugins/scaffolder-backend-module-azure/report.api.md +++ b/plugins/scaffolder-backend-module-azure/report.api.md @@ -27,7 +27,9 @@ export function createPublishAzureAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }, - JsonObject + JsonObject, + 'v1' >; ``` diff --git a/plugins/scaffolder-backend-module-azure/src/actions/azure.ts b/plugins/scaffolder-backend-module-azure/src/actions/azure.ts index 518d2acbf7..acb4754520 100644 --- a/plugins/scaffolder-backend-module-azure/src/actions/azure.ts +++ b/plugins/scaffolder-backend-module-azure/src/actions/azure.ts @@ -20,10 +20,10 @@ import { ScmIntegrationRegistry, } from '@backstage/integration'; import { - initRepoAndPush, - getRepoSourceDirectory, - parseRepoUrl, createTemplateAction, + getRepoSourceDirectory, + initRepoAndPush, + parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; import { GitRepositoryCreateOptions } from 'azure-devops-node-api/interfaces/GitInterfaces'; import { @@ -54,6 +54,7 @@ export function createPublishAzureAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }>({ id: 'publish:azure', examples, @@ -103,6 +104,11 @@ export function createPublishAzureAction(options: { type: 'string', description: 'The token to use for authorization to Azure', }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, }, }, output: { @@ -134,6 +140,7 @@ export function createPublishAzureAction(options: { gitCommitMessage = 'initial commit', gitAuthorName, gitAuthorEmail, + signCommit, } = ctx.input; const { project, repo, host, organization } = parseRepoUrl( @@ -151,6 +158,7 @@ export function createPublishAzureAction(options: { const credentialProvider = DefaultAzureDevOpsCredentialsProvider.fromIntegrations(integrations); const credentials = await credentialProvider.getCredentials({ url: url }); + const integrationConfig = integrations.azure.byHost(host); if (credentials === undefined && ctx.input.token === undefined) { throw new InputError( @@ -212,6 +220,15 @@ export function createPublishAzureAction(options: { password: ctx.input.token ?? credentials!.token, }; + const signingKey = + integrationConfig?.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } + const commitResult = await initRepoAndPush({ dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), remoteUrl, @@ -222,6 +239,7 @@ export function createPublishAzureAction(options: { ? gitCommitMessage : config.getOptionalString('scaffolder.defaultCommitMessage'), gitAuthorInfo, + signingKey: signCommit ? signingKey : undefined, }); ctx.output('commitHash', commitResult?.commitHash); diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md index 600f54c508..4d8a71f12a 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-cloud +## 0.2.7-next.2 + +### Patch Changes + +- c56a279: Added `bitbucketCloud:branchRestriction:create` to allow users to create bitbucket cloud branch restrictions in templates +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- e279c30: Fixing spelling mistake in `jsonschema` +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-bitbucket-cloud-common@0.2.28-next.0 + ## 0.2.7-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json index babc74e1ae..ebb0219343 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.2.7-next.1", + "version": "0.2.7-next.2", "description": "The Bitbucket Cloud module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/report.api.md b/plugins/scaffolder-backend-module-bitbucket-cloud/report.api.md index ad74479b6e..8deda92860 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/report.api.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/report.api.md @@ -23,7 +23,12 @@ export const createBitbucketPipelinesRunAction: (options: { body?: object; token?: string; }, - JsonObject + { + buildNumber: number; + repoUrl: string; + pipelinesUrl: string; + }, + 'v1' >; // @public @@ -39,8 +44,10 @@ export function createPublishBitbucketCloudAction(options: { gitCommitMessage?: string; sourcePath?: string; token?: string; + signCommit?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -58,6 +65,7 @@ export function createPublishBitbucketCloudPullRequestAction(options: { gitAuthorName?: string; gitAuthorEmail?: string; }, - JsonObject + JsonObject, + 'v1' >; ``` diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloud.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloud.ts index 00c27f6a54..945aab1216 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloud.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloud.ts @@ -18,8 +18,8 @@ import { InputError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { createTemplateAction, - initRepoAndPush, getRepoSourceDirectory, + initRepoAndPush, parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; @@ -113,6 +113,7 @@ export function createPublishBitbucketCloudAction(options: { gitCommitMessage?: string; sourcePath?: string; token?: string; + signCommit?: boolean; }>({ id: 'publish:bitbucketCloud', examples, @@ -158,6 +159,11 @@ export function createPublishBitbucketCloudAction(options: { description: 'The token to use for authorization to BitBucket Cloud', }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, }, }, output: { @@ -185,6 +191,7 @@ export function createPublishBitbucketCloudAction(options: { defaultBranch = 'master', gitCommitMessage, repoVisibility = 'private', + signCommit, } = ctx.input; const { workspace, project, repo, host } = parseRepoUrl( @@ -256,6 +263,15 @@ export function createPublishBitbucketCloudAction(options: { }; } + const signingKey = + integrationConfig.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } + const commitResult = await initRepoAndPush({ dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), remoteUrl, @@ -266,6 +282,7 @@ export function createPublishBitbucketCloudAction(options: { gitCommitMessage || config.getOptionalString('scaffolder.defaultCommitMessage'), gitAuthorInfo, + signingKey: signCommit ? signingKey : undefined, }); ctx.output('commitHash', commitResult?.commitHash); diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPipelinesRun.test.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPipelinesRun.test.ts index edabe2a7ba..83d959e67f 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPipelinesRun.test.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPipelinesRun.test.ts @@ -65,9 +65,11 @@ describe('bitbucket:pipelines:run', () => { const actionNoCreds = createBitbucketPipelinesRunAction({ integrations: integrationsNoCreds, }); + const testContext = Object.assign({}, mockContext, { input: { workspace, repo_slug }, }); + await expect(actionNoCreds.handler(testContext)).rejects.toThrow( /Authorization has not been provided for Bitbucket Cloud/, ); diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPipelinesRun.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPipelinesRun.ts index 8525774692..11064f4743 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPipelinesRun.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPipelinesRun.ts @@ -30,12 +30,19 @@ export const createBitbucketPipelinesRunAction = (options: { integrations: ScmIntegrationRegistry; }) => { const { integrations } = options; - return createTemplateAction<{ - workspace: string; - repo_slug: string; - body?: object; - token?: string; - }>({ + return createTemplateAction< + { + workspace: string; + repo_slug: string; + body?: object; + token?: string; + }, + { + buildNumber: number; + repoUrl: string; + pipelinesUrl: string; + } + >({ id, description: 'Run a bitbucket cloud pipeline', examples, @@ -58,7 +65,7 @@ export const createBitbucketPipelinesRunAction = (options: { type: 'number', }, repoUrl: { - title: 'A URL to the pipeline repositry', + title: 'A URL to the pipeline repository', type: 'string', }, repoContentsUrl: { diff --git a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md index b86ddc4e18..16544db6b9 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-server +## 0.2.7-next.2 + +### Patch Changes + +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.2.7-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-server/package.json b/plugins/scaffolder-backend-module-bitbucket-server/package.json index b5feb22b98..dd996891c5 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.2.7-next.1", + "version": "0.2.7-next.2", "description": "The Bitbucket Server module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-server/report.api.md b/plugins/scaffolder-backend-module-bitbucket-server/report.api.md index e92c85b284..2f4d68c95d 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/report.api.md +++ b/plugins/scaffolder-backend-module-bitbucket-server/report.api.md @@ -29,8 +29,10 @@ export function createPublishBitbucketServerAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -49,6 +51,7 @@ export function createPublishBitbucketServerPullRequestAction(options: { gitAuthorName?: string; gitAuthorEmail?: string; }, - JsonObject + JsonObject, + 'v1' >; ``` diff --git a/plugins/scaffolder-backend-module-bitbucket-server/src/actions/bitbucketServer.ts b/plugins/scaffolder-backend-module-bitbucket-server/src/actions/bitbucketServer.ts index 612d090547..3f50d5f274 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/src/actions/bitbucketServer.ts +++ b/plugins/scaffolder-backend-module-bitbucket-server/src/actions/bitbucketServer.ts @@ -21,8 +21,8 @@ import { } from '@backstage/integration'; import { createTemplateAction, - initRepoAndPush, getRepoSourceDirectory, + initRepoAndPush, parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; @@ -137,6 +137,7 @@ export function createPublishBitbucketServerAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }>({ id: 'publish:bitbucketServer', description: @@ -197,6 +198,11 @@ export function createPublishBitbucketServerAction(options: { type: 'string', description: `Sets the author email for the commit.`, }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, }, }, output: { @@ -227,6 +233,7 @@ export function createPublishBitbucketServerAction(options: { gitCommitMessage = 'initial commit', gitAuthorName, gitAuthorEmail, + signCommit, } = ctx.input; const { project, repo, host } = parseRepoUrl(repoUrl, integrations); @@ -279,6 +286,15 @@ export function createPublishBitbucketServerAction(options: { : config.getOptionalString('scaffolder.defaultAuthor.email'), }; + const signingKey = + integrationConfig.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } + const auth = authConfig.token ? { token: token!, @@ -298,6 +314,7 @@ export function createPublishBitbucketServerAction(options: { ? gitCommitMessage : config.getOptionalString('scaffolder.defaultCommitMessage'), gitAuthorInfo, + signingKey: signCommit ? signingKey : undefined, }); if (enableLFS) { diff --git a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md index 0a27f73db8..3edb45d23b 100644 --- a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket +## 0.3.8-next.2 + +### Patch Changes + +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.7-next.2 + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.7-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.3.8-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket/package.json b/plugins/scaffolder-backend-module-bitbucket/package.json index ee8bcd052b..5486c1b1c4 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.3.8-next.1", + "version": "0.3.8-next.2", "description": "The bitbucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket/report.api.md b/plugins/scaffolder-backend-module-bitbucket/report.api.md index b90f2f8483..dd45ae5c01 100644 --- a/plugins/scaffolder-backend-module-bitbucket/report.api.md +++ b/plugins/scaffolder-backend-module-bitbucket/report.api.md @@ -25,7 +25,12 @@ export const createBitbucketPipelinesRunAction: (options: { body?: object; token?: string; }, - JsonObject + { + buildNumber: number; + repoUrl: string; + pipelinesUrl: string; + }, + 'v1' >; // @public @deprecated @@ -44,8 +49,10 @@ export function createPublishBitbucketAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public @deprecated (undocumented) diff --git a/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.ts b/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.ts index 7a58efa10a..e52eb50976 100644 --- a/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.ts +++ b/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.ts @@ -21,8 +21,8 @@ import { } from '@backstage/integration'; import { createTemplateAction, - initRepoAndPush, getRepoSourceDirectory, + initRepoAndPush, parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; import { Config } from '@backstage/config'; @@ -220,6 +220,7 @@ export function createPublishBitbucketAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }>({ id: 'publish:bitbucket', description: @@ -280,6 +281,11 @@ export function createPublishBitbucketAction(options: { type: 'string', description: `Sets the default author email for the commit.`, }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, }, }, output: { @@ -313,6 +319,7 @@ export function createPublishBitbucketAction(options: { gitCommitMessage = 'initial commit', gitAuthorName, gitAuthorEmail, + signCommit, } = ctx.input; const { workspace, project, repo, host } = parseRepoUrl( @@ -380,6 +387,14 @@ export function createPublishBitbucketAction(options: { ? gitAuthorEmail : config.getOptionalString('scaffolder.defaultAuthor.email'), }; + const signingKey = + integrationConfig.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } let auth; @@ -409,6 +424,7 @@ export function createPublishBitbucketAction(options: { ? gitCommitMessage : config.getOptionalString('scaffolder.defaultCommitMessage'), gitAuthorInfo, + signingKey: signCommit ? signingKey : undefined, }); if (enableLFS && host !== 'bitbucket.org') { diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md index 9d16edd097..efbabd7d75 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-confluence-to-markdown +## 0.3.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.3.7-next.1 ### 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 0c65f2f539..16c241cc70 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.3.7-next.1", + "version": "0.3.7-next.2", "description": "The confluence-to-markdown module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/report.api.md b/plugins/scaffolder-backend-module-confluence-to-markdown/report.api.md index 2dd5e244dd..64584ad4b5 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/report.api.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/report.api.md @@ -24,6 +24,7 @@ export const createConfluenceToMarkdownAction: (options: { confluenceUrls: string[]; repoUrl: string; }, - JsonObject + JsonObject, + 'v1' >; ``` diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index 99de1d28d5..2a08023971 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.3.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.3.8-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 69b972b46c..706c1d8340 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.3.8-next.1", + "version": "0.3.8-next.2", "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-cookiecutter/report.api.md b/plugins/scaffolder-backend-module-cookiecutter/report.api.md index 36339ea722..4f963ce818 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/report.api.md +++ b/plugins/scaffolder-backend-module-cookiecutter/report.api.md @@ -54,6 +54,7 @@ export function createFetchCookiecutterAction(options: { extensions?: string[]; imageName?: string; }, - JsonObject + JsonObject, + 'v1' >; ``` diff --git a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md index 85cc405ecf..28baa5b983 100644 --- a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-gcp +## 0.2.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.2.7-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gcp/package.json b/plugins/scaffolder-backend-module-gcp/package.json index c9430baea7..2c71d05ca1 100644 --- a/plugins/scaffolder-backend-module-gcp/package.json +++ b/plugins/scaffolder-backend-module-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gcp", - "version": "0.2.7-next.1", + "version": "0.2.7-next.2", "description": "The GCP Bucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md index 910b3f5b51..2a7c4e7b86 100644 --- a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-gerrit +## 0.2.7-next.2 + +### Patch Changes + +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.2.7-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gerrit/package.json b/plugins/scaffolder-backend-module-gerrit/package.json index 4e5b9f87b7..a77d362fda 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.2.7-next.1", + "version": "0.2.7-next.2", "description": "The gerrit module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gerrit/report.api.md b/plugins/scaffolder-backend-module-gerrit/report.api.md index a3f23d7aa7..937df6f435 100644 --- a/plugins/scaffolder-backend-module-gerrit/report.api.md +++ b/plugins/scaffolder-backend-module-gerrit/report.api.md @@ -22,8 +22,10 @@ export function createPublishGerritAction(options: { gitAuthorName?: string; gitAuthorEmail?: string; sourcePath?: string; + signCommit?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -38,8 +40,10 @@ export function createPublishGerritReviewAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public diff --git a/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.ts b/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.ts index db0ae2ba70..23f28ae934 100644 --- a/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.ts +++ b/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.ts @@ -24,8 +24,8 @@ import { } from '@backstage/integration'; import { createTemplateAction, - initRepoAndPush, getRepoSourceDirectory, + initRepoAndPush, parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; import { examples } from './gerrit.examples'; @@ -99,6 +99,7 @@ export function createPublishGerritAction(options: { gitAuthorName?: string; gitAuthorEmail?: string; sourcePath?: string; + signCommit?: boolean; }>({ id: 'publish:gerrit', supportsDryRun: true, @@ -143,6 +144,11 @@ export function createPublishGerritAction(options: { type: 'string', description: `Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.`, }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, }, }, output: { @@ -172,6 +178,7 @@ export function createPublishGerritAction(options: { gitAuthorEmail, gitCommitMessage = 'initial commit', sourcePath, + signCommit, } = ctx.input; const { repo, host, owner, workspace } = parseRepoUrl( repoUrl, @@ -233,6 +240,15 @@ export function createPublishGerritAction(options: { email: gitEmail, }; + const signingKey = + integrationConfig.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } + const commitResult = await initRepoAndPush({ dir: getRepoSourceDirectory(ctx.workspacePath, sourcePath), remoteUrl, @@ -241,6 +257,7 @@ export function createPublishGerritAction(options: { logger: ctx.logger, commitMessage: generateCommitMessage(config, gitCommitMessage), gitAuthorInfo, + signingKey: signCommit ? signingKey : undefined, }); ctx.output('remoteUrl', remoteUrl); diff --git a/plugins/scaffolder-backend-module-gerrit/src/actions/gerritReview.ts b/plugins/scaffolder-backend-module-gerrit/src/actions/gerritReview.ts index b4f844b08f..b4ab3afef7 100644 --- a/plugins/scaffolder-backend-module-gerrit/src/actions/gerritReview.ts +++ b/plugins/scaffolder-backend-module-gerrit/src/actions/gerritReview.ts @@ -19,8 +19,8 @@ import { InputError } from '@backstage/errors'; import { Config } from '@backstage/config'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { - createTemplateAction, commitAndPushRepo, + createTemplateAction, getRepoSourceDirectory, parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; @@ -48,6 +48,7 @@ export function createPublishGerritReviewAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }>({ id: 'publish:gerrit:review', description: 'Creates a new Gerrit review.', @@ -88,6 +89,11 @@ export function createPublishGerritReviewAction(options: { type: 'string', description: `Sets the default author email for the commit.`, }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, }, }, output: { @@ -112,6 +118,7 @@ export function createPublishGerritReviewAction(options: { gitAuthorName, gitAuthorEmail, gitCommitMessage, + signCommit, } = ctx.input; const { host, repo } = parseRepoUrl(repoUrl, integrations); @@ -139,6 +146,14 @@ export function createPublishGerritReviewAction(options: { ? gitAuthorEmail : config.getOptionalString('scaffolder.defaultAuthor.email'), }; + const signingKey = + integrationConfig.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } const changeId = generateGerritChangeId(); const commitMessage = `${gitCommitMessage}\n\nChange-Id: ${changeId}`; @@ -150,6 +165,7 @@ export function createPublishGerritReviewAction(options: { gitAuthorInfo, branch, remoteRef: `refs/for/${branch}`, + signingKey: signCommit ? signingKey : undefined, }); const repoContentsUrl = `${integrationConfig.config.gitilesBaseUrl}/${repo}/+/refs/heads/${branch}`; diff --git a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md index ebf8690a10..b5b47ec622 100644 --- a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-gitea +## 0.2.7-next.2 + +### Patch Changes + +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.2.7-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index 310d2524d1..7cc94b06bc 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.2.7-next.1", + "version": "0.2.7-next.2", "description": "The gitea module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gitea/report.api.md b/plugins/scaffolder-backend-module-gitea/report.api.md index 8456b7183c..dede7f013a 100644 --- a/plugins/scaffolder-backend-module-gitea/report.api.md +++ b/plugins/scaffolder-backend-module-gitea/report.api.md @@ -23,8 +23,10 @@ export function createPublishGiteaAction(options: { gitAuthorName?: string; gitAuthorEmail?: string; sourcePath?: string; + signCommit?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index 6b29ff2a4c..4321de2167 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -219,6 +219,7 @@ export function createPublishGiteaAction(options: { gitAuthorName?: string; gitAuthorEmail?: string; sourcePath?: string; + signCommit?: boolean; }>({ id: 'publish:gitea', description: @@ -268,6 +269,11 @@ export function createPublishGiteaAction(options: { type: 'string', description: `Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.`, }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, }, }, output: { @@ -298,6 +304,7 @@ export function createPublishGiteaAction(options: { gitAuthorEmail, gitCommitMessage = 'initial commit', sourcePath, + signCommit, } = ctx.input; const { repo, host, owner } = parseRepoUrl(repoUrl, integrations); @@ -338,6 +345,16 @@ export function createPublishGiteaAction(options: { ? gitAuthorEmail : config.getOptionalString('scaffolder.defaultAuthor.email'), }; + + const signingKey = + integrationConfig.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } + // The owner to be used should be either the org name or user authenticated with the gitea server const remoteUrl = `${integrationConfig.config.baseUrl}/${owner}/${repo}.git`; const commitResult = await initRepoAndPush({ diff --git a/plugins/scaffolder-backend-module-github/CHANGELOG.md b/plugins/scaffolder-backend-module-github/CHANGELOG.md index 6cd45a3f88..d5f476d936 100644 --- a/plugins/scaffolder-backend-module-github/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-github/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder-backend-module-github +## 0.6.1-next.2 + +### Patch Changes + +- 9391f58: Pass `undefined` to some parameters for `createOrUpdateEnvironment` as these values are not always supported in different plans of GitHub +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.6.1-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-github/package.json b/plugins/scaffolder-backend-module-github/package.json index 613ca84ff1..9ca633e659 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.6.1-next.1", + "version": "0.6.1-next.2", "description": "The github module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-github/report.api.md b/plugins/scaffolder-backend-module-github/report.api.md index e2d124eb6f..f1b7f98a30 100644 --- a/plugins/scaffolder-backend-module-github/report.api.md +++ b/plugins/scaffolder-backend-module-github/report.api.md @@ -30,7 +30,8 @@ export function createGithubActionsDispatchAction(options: { }; token?: string; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -45,7 +46,8 @@ export function createGithubAutolinksAction(options: { isAlphanumeric?: boolean; token?: string; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -81,7 +83,8 @@ export function createGithubBranchProtectionAction(options: { requiredLinearHistory?: boolean; token?: string; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -96,7 +99,8 @@ export function createGithubDeployKeyAction(options: { privateKeySecretName?: string; token?: string; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -125,7 +129,8 @@ export function createGithubEnvironmentAction(options: { preventSelfReview?: boolean; reviewers?: string[]; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -139,7 +144,8 @@ export function createGithubIssuesLabelAction(options: { labels: string[]; token?: string; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -154,7 +160,8 @@ export function createGithubPagesEnableAction(options: { sourcePath?: '/' | '/docs'; token?: string; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -255,7 +262,8 @@ export function createGithubRepoCreateAction(options: { }; subscribe?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -299,7 +307,8 @@ export function createGithubRepoPushAction(options: { requiredLinearHistory?: boolean; requireLastPushApproval?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -318,7 +327,8 @@ export function createGithubWebhookAction(options: { insecureSsl?: boolean; token?: string; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -404,7 +414,8 @@ export function createPublishGithubAction(options: { }; subscribe?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -431,7 +442,8 @@ export const createPublishGithubPullRequestAction: ( forceEmptyGitAuthor?: boolean; createWhenEmpty?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public diff --git a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md index bd8c6d338b..0269ab61d4 100644 --- a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-scaffolder-backend-module-gitlab +## 0.8.1-next.2 + +### Patch Changes + +- 0df33ea: fix: Creating a repository in a user namespace would always lead to an error +- ac58f84: Made gitlab:issue:edit action idempotent. +- a75e18f: Change the if statement in the catch block to match what the new version of Gitbeaker will return +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.8.1-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index 4c52bc9ee0..a33c3f5d8e 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.8.1-next.1", + "version": "0.8.1-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-gitlab/report.api.md b/plugins/scaffolder-backend-module-gitlab/report.api.md index d50104e98e..cdb6065a3b 100644 --- a/plugins/scaffolder-backend-module-gitlab/report.api.md +++ b/plugins/scaffolder-backend-module-gitlab/report.api.md @@ -26,7 +26,8 @@ export const createGitlabGroupEnsureExistsAction: (options: { }, { groupId?: number | undefined; - } + }, + 'v1' >; // @public @@ -55,7 +56,8 @@ export const createGitlabIssueAction: (options: { issueUrl: string; issueId: number; issueIid: number; - } + }, + 'v1' >; // @public @@ -73,7 +75,8 @@ export const createGitlabProjectAccessTokenAction: (options: { }, { access_token: string; - } + }, + 'v1' >; // @public @@ -91,7 +94,8 @@ export const createGitlabProjectDeployTokenAction: (options: { { user: string; deploy_token: string; - } + }, + 'v1' >; // @public @@ -110,7 +114,8 @@ export const createGitlabProjectVariableAction: (options: { environmentScope?: string | undefined; variableProtected?: boolean | undefined; }, - JsonObject + any, + 'v1' >; // @public @@ -126,7 +131,8 @@ export const createGitlabRepoPushAction: (options: { token?: string; commitAction?: 'create' | 'delete' | 'update'; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -144,6 +150,7 @@ export function createPublishGitlabAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; setUserAsOwner?: boolean; topics?: string[]; settings?: { @@ -176,7 +183,8 @@ export function createPublishGitlabAction(options: { environment_scope?: string; }>; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -199,7 +207,8 @@ export const createPublishGitlabMergeRequestAction: (options: { reviewers?: string[]; assignReviewersFromApprovalRules?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -216,7 +225,8 @@ export const createTriggerGitlabPipelineAction: (options: { }, { pipelineUrl: string; - } + }, + 'v1' >; // @public @@ -252,7 +262,8 @@ export const editGitlabIssueAction: (options: { issueUrl: string; issueId: number; issueIid: number; - } + }, + 'v1' >; // @public diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.test.ts index e255652f15..1e3ac40d35 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.test.ts @@ -44,6 +44,7 @@ const mockGitlabClient = { }, Users: { showCurrentUser: jest.fn(), + allProjects: jest.fn(), }, ProjectMembers: { add: jest.fn(), @@ -74,6 +75,7 @@ describe('publish:gitlab', () => { host: 'gitlab.com', token: 'tokenlols', apiBaseUrl: 'https://api.gitlab.com', + commitSigningKey: 'test-signing-key', }, { host: 'hosted.gitlab.com', @@ -211,7 +213,7 @@ describe('publish:gitlab', () => { expect(mockGitlabClient.ProtectedBranches.protect).not.toHaveBeenCalled(); }); - it('should call the correct Gitlab APIs when the owner is an organization', async () => { + it('should call the correct Gitlab APIs when the owner is a group', async () => { mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); mockGitlabClient.Groups.allProjects.mockResolvedValue([]); @@ -232,16 +234,18 @@ describe('publish:gitlab', () => { expect(mockGitlabClient.ProtectedBranches.protect).not.toHaveBeenCalled(); }); - it('should call the correct Gitlab APIs when the owner is not an organization', async () => { + it('should call the correct Gitlab APIs when the owner is a user', async () => { mockGitlabClient.Namespaces.show.mockResolvedValue({ id: null }); mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); - mockGitlabClient.Groups.allProjects.mockResolvedValue([]); + mockGitlabClient.Groups.allProjects.mockRejectedValue({}); + mockGitlabClient.Users.allProjects.mockResolvedValue([]); mockGitlabClient.Projects.create.mockResolvedValue({ http_url_to_repo: 'http://mockurl.git', }); await action.handler(mockContext); + expect(mockGitlabClient.Groups.allProjects).not.toHaveBeenCalled(); expect(mockGitlabClient.Namespaces.show).toHaveBeenCalledWith('owner'); expect(mockGitlabClient.Projects.create).toHaveBeenCalledWith({ namespaceId: 12345, @@ -253,9 +257,12 @@ describe('publish:gitlab', () => { expect(mockGitlabClient.ProtectedBranches.protect).not.toHaveBeenCalled(); }); - it('should not call the creation Gitlab APIs when the repository already exists', async () => { + it('should not call the creation Gitlab APIs when the repository already exists in a group', async () => { mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); - mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ + id: 1234, + kind: 'group', + }); mockGitlabClient.Groups.allProjects.mockResolvedValue([ { path: 'repo', @@ -278,9 +285,41 @@ describe('publish:gitlab', () => { expect(mockGitlabClient.ProtectedBranches.protect).not.toHaveBeenCalled(); }); + it('should not call the creation Gitlab APIs when the repository already exists in a user namespace', async () => { + mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ + id: 12345, + kind: 'user', + }); + mockGitlabClient.Users.allProjects.mockResolvedValue([ + { + path: 'repo', + http_url_to_repo: 'http://mockurl.git', + }, + { + path: 'repo-name', + http_url_to_repo: 'http://mockurl.git', + }, + ]); + + await action.handler({ + ...mockContext, + input: { ...mockContext.input, skipExisting: true }, + }); + + expect(mockGitlabClient.Groups.allProjects).not.toHaveBeenCalled(); + expect(mockGitlabClient.Namespaces.show).toHaveBeenCalledWith('owner'); + expect(mockGitlabClient.Projects.create).not.toHaveBeenCalled(); + expect(mockGitlabClient.Branches.create).not.toHaveBeenCalled(); + expect(mockGitlabClient.ProtectedBranches.protect).not.toHaveBeenCalled(); + }); + it('should call the creation Gitlab APIs when the repository does not yet exists', async () => { mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); - mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ + id: 1234, + kind: 'group', + }); mockGitlabClient.Groups.allProjects.mockResolvedValue([ { path: 'repo-name', @@ -305,7 +344,10 @@ describe('publish:gitlab', () => { it('should call the correct Gitlab APIs when using project settings with override of visibility and topics', async () => { mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); - mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ + id: 1234, + kind: 'group', + }); mockGitlabClient.Groups.allProjects.mockResolvedValue([]); mockGitlabClient.Projects.create.mockResolvedValue({ http_url_to_repo: 'http://mockurl.git', @@ -327,7 +369,10 @@ describe('publish:gitlab', () => { it('should call the correct Gitlab APIs for branches and protectd branches when branch settings provided', async () => { mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); - mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ + id: 1234, + kind: 'group', + }); mockGitlabClient.Groups.allProjects.mockResolvedValue([]); mockGitlabClient.Projects.create.mockResolvedValue({ id: 123456, @@ -368,7 +413,10 @@ describe('publish:gitlab', () => { it('should call the correct Gitlab APIs for variables when their configuration is provided', async () => { mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); - mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ + id: 1234, + kind: 'group', + }); mockGitlabClient.Groups.allProjects.mockResolvedValue([]); mockGitlabClient.Projects.create.mockResolvedValue({ id: 123456, @@ -401,7 +449,10 @@ describe('publish:gitlab', () => { it('should call initRepoAndPush with the correct values', async () => { mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); - mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ + id: 1234, + kind: 'group', + }); mockGitlabClient.Groups.allProjects.mockResolvedValue([]); mockGitlabClient.Projects.create.mockResolvedValue({ http_url_to_repo: 'http://mockurl.git', @@ -420,7 +471,7 @@ describe('publish:gitlab', () => { }); }); - it('should not call initRepoAndPush when sourcePath is false', async () => { + it('should call initRepoAndPush with the signing key', async () => { mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); mockGitlabClient.Groups.allProjects.mockResolvedValue([]); @@ -428,6 +479,34 @@ describe('publish:gitlab', () => { http_url_to_repo: 'http://mockurl.git', }); + await action.handler({ + ...mockContext, + input: { ...mockContext.input, signCommit: true }, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + defaultBranch: 'master', + remoteUrl: 'http://mockurl.git', + auth: { username: 'oauth2', password: 'tokenlols' }, + logger: mockContext.logger, + commitMessage: 'initial commit', + gitAuthorInfo: {}, + signingKey: 'test-signing-key', + }); + }); + + it('should not call initRepoAndPush when sourcePath is false', async () => { + mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ + id: 1234, + kind: 'group', + }); + mockGitlabClient.Groups.allProjects.mockResolvedValue([]); + mockGitlabClient.Projects.create.mockResolvedValue({ + http_url_to_repo: 'http://mockurl.git', + }); + await action.handler({ ...mockContext, input: { ...mockContext.input, sourcePath: false }, @@ -446,7 +525,10 @@ describe('publish:gitlab', () => { it('should call initRepoAndPush with the correct default branch', async () => { mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); - mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ + id: 1234, + kind: 'group', + }); mockGitlabClient.Groups.allProjects.mockResolvedValue([]); mockGitlabClient.Projects.create.mockResolvedValue({ http_url_to_repo: 'http://mockurl.git', @@ -502,7 +584,10 @@ describe('publish:gitlab', () => { }); mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); - mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ + id: 1234, + kind: 'group', + }); mockGitlabClient.Groups.allProjects.mockResolvedValue([]); mockGitlabClient.Projects.create.mockResolvedValue({ http_url_to_repo: 'http://mockurl.git', @@ -549,7 +634,10 @@ describe('publish:gitlab', () => { }); mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); - mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ + id: 1234, + kind: 'group', + }); mockGitlabClient.Groups.allProjects.mockResolvedValue([]); mockGitlabClient.Projects.create.mockResolvedValue({ http_url_to_repo: 'http://mockurl.git', @@ -570,7 +658,10 @@ describe('publish:gitlab', () => { it('should call output with the remoteUrl and repoContentsUrl and projectId', async () => { mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); - mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ + id: 1234, + kind: 'group', + }); mockGitlabClient.Groups.allProjects.mockResolvedValue([]); mockGitlabClient.Projects.create.mockResolvedValue({ http_url_to_repo: 'http://mockurl.git', @@ -610,7 +701,10 @@ describe('publish:gitlab', () => { config: customAuthorConfig, }); - mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ + id: 1234, + kind: 'group', + }); mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); mockGitlabClient.Groups.allProjects.mockResolvedValue([]); mockGitlabClient.Projects.create.mockResolvedValue({ diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.ts index b2641e6656..753da2e253 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.ts @@ -16,13 +16,13 @@ import { InputError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; -import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; -import { Gitlab, VariableType } from '@gitbeaker/rest'; import { - initRepoAndPush, + createTemplateAction, getRepoSourceDirectory, + initRepoAndPush, parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; +import { Gitlab, VariableType } from '@gitbeaker/rest'; import { Config } from '@backstage/config'; import { examples } from './gitlab.examples'; @@ -49,6 +49,7 @@ export function createPublishGitlabAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; setUserAsOwner?: boolean; /** @deprecated in favour of settings.topics field */ topics?: string[]; @@ -122,6 +123,11 @@ export function createPublishGitlabAction(options: { type: 'string', description: `Sets the default author email for the commit.`, }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, sourcePath: { title: 'Source Path', description: @@ -349,6 +355,7 @@ export function createPublishGitlabAction(options: { branches = [], projectVariables = [], skipExisting = false, + signCommit, } = ctx.input; const { owner, repo, host } = parseRepoUrl(repoUrl, integrations); @@ -379,13 +386,15 @@ export function createPublishGitlabAction(options: { }); let targetNamespaceId; - + let targetNamespaceKind; try { const namespaceResponse = (await client.Namespaces.show(owner)) as { id: number; + kind: string; }; targetNamespaceId = namespaceResponse.id; + targetNamespaceKind = namespaceResponse.kind; } catch (e) { if (e.cause?.response?.status === 404) { throw new InputError( @@ -401,13 +410,16 @@ export function createPublishGitlabAction(options: { if (!targetNamespaceId) { targetNamespaceId = userId; + targetNamespaceKind = 'user'; } - const existingProjects = await client.Groups.allProjects(owner, { - search: repo, - }); + const existingProjects = + targetNamespaceKind === 'user' + ? await client.Users.allProjects(owner, { search: repo }) + : await client.Groups.allProjects(owner, { search: repo }); + const existingProject = existingProjects.find( - searchPathElem => searchPathElem.path === repo, + project => project.path === repo, ); if (!skipExisting || (skipExisting && !existingProject)) { @@ -447,6 +459,15 @@ export function createPublishGitlabAction(options: { ? gitAuthorEmail : config.getOptionalString('scaffolder.defaultAuthor.email'), }; + const signingKey = + integrationConfig.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } + const shouldSkipPublish = typeof ctx.input.sourcePath === 'boolean' && !ctx.input.sourcePath; if (!shouldSkipPublish) { @@ -469,6 +490,7 @@ export function createPublishGitlabAction(options: { ? gitCommitMessage : config.getOptionalString('scaffolder.defaultCommitMessage'), gitAuthorInfo, + signingKey: signCommit ? signingKey : undefined, }); if (branches) { diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabRepoPush.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabRepoPush.ts index 6537c42315..f347aaadd0 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabRepoPush.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabRepoPush.ts @@ -151,19 +151,24 @@ export const createGitlabRepoPushAction = (options: { execute_filemode: file.executable, })); - let branchExists = false; - try { - await api.Branches.show(repoID, branchName); - branchExists = true; - } catch (e: any) { - if (e.cause?.response?.status !== 404) { - throw new InputError( - `Failed to check status of branch '${branchName}'. Please make sure that branch already exists or Backstage has permissions to create one. ${getErrorMessage( - e, - )}`, - ); - } - } + const branchExists = await ctx.checkpoint({ + key: `branch.exists.${repoID}.${branchName}`, + fn: async () => { + try { + await api.Branches.show(repoID, branchName); + return true; + } catch (e: any) { + if (e.cause?.response?.status !== 404) { + throw new InputError( + `Failed to check status of branch '${branchName}'. Please make sure that branch already exists or Backstage has permissions to create one. ${getErrorMessage( + e, + )}`, + ); + } + } + return false; + }, + }); if (!branchExists) { // create a branch using the default branch as ref @@ -181,15 +186,22 @@ export const createGitlabRepoPushAction = (options: { } try { - const commit = await api.Commits.create( - repoID, - branchName, - ctx.input.commitMessage, - actions, - ); + const commitId = await ctx.checkpoint({ + key: `commit.create.${repoID}.${branchName}`, + fn: async () => { + const commit = await api.Commits.create( + repoID, + branchName, + ctx.input.commitMessage, + actions, + ); + return commit.id; + }, + }); + ctx.output('projectid', repoID); ctx.output('projectPath', repoID); - ctx.output('commitHash', commit.id); + ctx.output('commitHash', commitId); } catch (e) { throw new InputError( `Committing the changes to ${branchName} failed. Please check that none of the files created by the template already exists. ${getErrorMessage( diff --git a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md index 85a914a998..a5d73e15d1 100644 --- a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-notifications +## 0.1.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.13-next.2 + ## 0.1.8-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-notifications/package.json b/plugins/scaffolder-backend-module-notifications/package.json index cb5af0c3a5..9dfdfc5e05 100644 --- a/plugins/scaffolder-backend-module-notifications/package.json +++ b/plugins/scaffolder-backend-module-notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-notifications", - "version": "0.1.8-next.1", + "version": "0.1.8-next.2", "description": "The notifications backend module for the scaffolder plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-notifications/report.api.md b/plugins/scaffolder-backend-module-notifications/report.api.md index 1c2bcf5f64..cb796e5033 100644 --- a/plugins/scaffolder-backend-module-notifications/report.api.md +++ b/plugins/scaffolder-backend-module-notifications/report.api.md @@ -23,7 +23,8 @@ export function createSendNotificationAction(options: { scope?: string; optional?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index 0ad330fc44..8b6c700405 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.5.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.5.7-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index 49f4f57329..1fe65455e3 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.5.7-next.1", + "version": "0.5.7-next.2", "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-rails/report.api.md b/plugins/scaffolder-backend-module-rails/report.api.md index 846e2fe77f..0d7618852d 100644 --- a/plugins/scaffolder-backend-module-rails/report.api.md +++ b/plugins/scaffolder-backend-module-rails/report.api.md @@ -49,7 +49,8 @@ export function createFetchRailsAction(options: { values: JsonObject; imageName?: string; }, - JsonObject + JsonObject, + 'v1' >; // @public diff --git a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md index a38d33d6d3..df4b05bc06 100644 --- a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-sentry +## 0.2.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.2.7-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index 4a4398f955..c5604e3679 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.2.7-next.1", + "version": "0.2.7-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-sentry/report.api.md b/plugins/scaffolder-backend-module-sentry/report.api.md index ff31bd62c3..973ef9844c 100644 --- a/plugins/scaffolder-backend-module-sentry/report.api.md +++ b/plugins/scaffolder-backend-module-sentry/report.api.md @@ -19,7 +19,8 @@ export function createSentryCreateProjectAction(options: { slug?: string; authToken?: string; }, - JsonObject + JsonObject, + 'v1' >; // @public diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index eff0d028d2..640a554001 100644 --- a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-yeoman +## 0.4.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/plugin-scaffolder-node-test-utils@0.2.0-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/types@1.2.1 + ## 0.4.8-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index b423b92b49..d1625abc96 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.4.8-next.1", + "version": "0.4.8-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-yeoman/report.api.md b/plugins/scaffolder-backend-module-yeoman/report.api.md index ac262abcf6..57498bbe3a 100644 --- a/plugins/scaffolder-backend-module-yeoman/report.api.md +++ b/plugins/scaffolder-backend-module-yeoman/report.api.md @@ -14,7 +14,8 @@ export function createRunYeomanAction(): TemplateAction< args?: string[]; options?: JsonObject; }, - JsonObject + JsonObject, + 'v1' >; // @public diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 8829813b77..900d1ef3b6 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,44 @@ # @backstage/plugin-scaffolder-backend +## 1.31.0-next.2 + +### Minor Changes + +- 36677bb: Support new `createTemplateAction` type, and convert `catalog:fetch` action to new way of defining actions. +- 2b1e50d: use CreatedTemplate[Filter|Global*] as canonical template extensions in scaffolder plugin + +### Patch Changes + +- e0b226b: build(deps): bump `esbuild` from 0.24.2 to 0.25.0 +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- 59dcf37: Fixed bug in fs:delete causing no files to be deleted on windows machines +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-gitlab@0.8.1-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.7-next.2 + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/plugin-scaffolder-backend-module-github@0.6.1-next.2 + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.7-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.8-next.2 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.7-next.2 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.7-next.2 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.7-next.2 + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-events-node@0.4.9-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-node@0.6.1-next.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.28-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.6-next.1 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-node@0.8.9-next.1 + - @backstage/plugin-scaffolder-common@1.5.10-next.0 + ## 1.30.1-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend/config.d.ts b/plugins/scaffolder-backend/config.d.ts index a391f97264..ae91c905b3 100644 --- a/plugins/scaffolder-backend/config.d.ts +++ b/plugins/scaffolder-backend/config.d.ts @@ -27,6 +27,11 @@ export interface Config { email?: string; }; + /** + * Default PGP signing key for signing commits. + * @visibility secret + */ + defaultCommitSigningKey?: string; /** * The commit message used when new components are created. */ diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 9e5fae8e3c..7107cecf82 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "1.30.1-next.1", + "version": "1.31.0-next.2", "description": "The Backstage backend plugin that helps you create new things", "backstage": { "role": "backend-plugin", @@ -97,7 +97,7 @@ "globby": "^11.0.0", "isbinaryfile": "^5.0.0", "isolated-vm": "^5.0.1", - "jsonschema": "^1.2.6", + "jsonschema": "^1.5.0", "knex": "^3.0.0", "lodash": "^4.17.21", "logform": "^2.3.2", diff --git a/plugins/scaffolder-backend/report.api.md b/plugins/scaffolder-backend/report.api.md index 18813154f2..37afe780a9 100644 --- a/plugins/scaffolder-backend/report.api.md +++ b/plugins/scaffolder-backend/report.api.md @@ -30,6 +30,7 @@ import { createPublishGerritAction as createPublishGerritAction_2 } from '@backs import { createPublishGerritReviewAction as createPublishGerritReviewAction_2 } from '@backstage/plugin-scaffolder-backend-module-gerrit'; import { createPublishGithubAction as createPublishGithubAction_2 } from '@backstage/plugin-scaffolder-backend-module-github'; import { createPublishGitlabAction as createPublishGitlabAction_2 } from '@backstage/plugin-scaffolder-backend-module-gitlab'; +import { createTemplateAction as createTemplateAction_2 } from '@backstage/plugin-scaffolder-node'; import { DatabaseService } from '@backstage/backend-plugin-api'; import { DiscoveryService } from '@backstage/backend-plugin-api'; import { Duration } from 'luxon'; @@ -54,7 +55,6 @@ import { RESOURCE_TYPE_SCAFFOLDER_ACTION } from '@backstage/plugin-scaffolder-co import { RESOURCE_TYPE_SCAFFOLDER_TEMPLATE } from '@backstage/plugin-scaffolder-common/alpha'; import { ScaffolderEntitiesProcessor as ScaffolderEntitiesProcessor_2 } from '@backstage/plugin-catalog-backend-module-scaffolder-entity-model'; import { SchedulerService } from '@backstage/backend-plugin-api'; -import { Schema } from 'jsonschema'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { ScmIntegrations } from '@backstage/integration'; import { SerializedTask as SerializedTask_2 } from '@backstage/plugin-scaffolder-node'; @@ -71,14 +71,12 @@ import { TaskSpec } from '@backstage/plugin-scaffolder-common'; import { TaskSpecV1beta3 } from '@backstage/plugin-scaffolder-common'; import { TaskStatus as TaskStatus_2 } from '@backstage/plugin-scaffolder-node'; import { TemplateAction as TemplateAction_2 } from '@backstage/plugin-scaffolder-node'; -import { TemplateActionOptions } from '@backstage/plugin-scaffolder-node'; import { TemplateEntityStepV1beta3 } from '@backstage/plugin-scaffolder-common'; import { TemplateFilter as TemplateFilter_2 } from '@backstage/plugin-scaffolder-node'; import { TemplateGlobal as TemplateGlobal_2 } from '@backstage/plugin-scaffolder-node'; import { TemplateParametersV1beta3 } from '@backstage/plugin-scaffolder-common'; import { UrlReaderService } from '@backstage/backend-plugin-api'; import { WorkspaceProvider } from '@backstage/plugin-scaffolder-node/alpha'; -import { ZodType } from 'zod'; // @public @deprecated (undocumented) export type ActionContext = ActionContext_2; @@ -125,7 +123,8 @@ export function createCatalogRegisterAction(options: { catalogInfoPath?: string; optional?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -134,17 +133,12 @@ export function createCatalogWriteAction(): TemplateAction_2< entity: Record; filePath?: string | undefined; }, - JsonObject + any, + 'v1' >; // @public -export function createDebugLogAction(): TemplateAction_2< - { - message?: string; - listWorkspace?: boolean | 'with-filenames' | 'with-contents'; - }, - JsonObject ->; +export function createDebugLogAction(): TemplateAction_2; // @public export function createFetchCatalogEntityAction(options: { @@ -152,16 +146,17 @@ export function createFetchCatalogEntityAction(options: { auth?: AuthService; }): TemplateAction_2< { + entityRef?: string | undefined; + entityRefs?: string[] | undefined; optional?: boolean | undefined; defaultKind?: string | undefined; defaultNamespace?: string | undefined; - entityRef?: string | undefined; - entityRefs?: string[] | undefined; }, { - entities?: any[] | undefined; entity?: any; - } + entities?: any[] | undefined; + }, + 'v2' >; // @public @@ -174,7 +169,8 @@ export function createFetchPlainAction(options: { targetPath?: string; token?: string; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -187,7 +183,8 @@ export function createFetchPlainFileAction(options: { targetPath: string; token?: string; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -210,7 +207,8 @@ export function createFetchTemplateAction(options: { lstripBlocks?: boolean; token?: string; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -230,7 +228,8 @@ export function createFetchTemplateFileAction(options: { lstripBlocks?: boolean; token?: string; }, - JsonObject + JsonObject, + 'v1' >; // @public @@ -238,14 +237,15 @@ export const createFilesystemDeleteAction: () => TemplateAction_2< { files: string[]; }, - JsonObject + JsonObject, + 'v1' >; // @public export const createFilesystemReadDirAction: () => TemplateAction_2< { + recursive: boolean; paths: string[]; - recursive?: boolean | undefined; }, { files: { @@ -258,7 +258,8 @@ export const createFilesystemReadDirAction: () => TemplateAction_2< path: string; fullPath: string; }[]; - } + }, + 'v1' >; // @public @@ -270,7 +271,8 @@ export const createFilesystemRenameAction: () => TemplateAction_2< overwrite?: boolean; }>; }, - JsonObject + JsonObject, + 'v1' >; // @public @deprecated (undocumented) @@ -346,7 +348,8 @@ export const createPublishGithubPullRequestAction: ( forceEmptyGitAuthor?: boolean; createWhenEmpty?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public @deprecated (undocumented) @@ -372,45 +375,20 @@ export const createPublishGitlabMergeRequestAction: (options: { reviewers?: string[]; assignReviewersFromApprovalRules?: boolean; }, - JsonObject + JsonObject, + 'v1' >; // @public @deprecated export function createRouter(options: RouterOptions): Promise; // @public @deprecated (undocumented) -export const createTemplateAction: < - TInputParams extends JsonObject = JsonObject, - TOutputParams extends JsonObject = JsonObject, - TInputSchema extends Schema | ZodType = {}, - TOutputSchema extends Schema | ZodType = {}, - TActionInput extends JsonObject = TInputSchema extends ZodType< - any, - any, - infer IReturn - > - ? IReturn - : TInputParams, - TActionOutput extends JsonObject = TOutputSchema extends ZodType< - any, - any, - infer IReturn_1 - > - ? IReturn_1 - : TOutputParams, ->( - action: TemplateActionOptions< - TActionInput, - TActionOutput, - TInputSchema, - TOutputSchema - >, -) => TemplateAction_2; +export const createTemplateAction: typeof createTemplateAction_2; // @public export function createWaitAction(options?: { maxWaitTime?: Duration | HumanDuration; -}): TemplateAction_2; +}): TemplateAction_2; // @public export type CreateWorkerOptions = { @@ -547,7 +525,7 @@ export const fetchContents: typeof fetchContents_2; // @public @deprecated export interface RouterOptions { // (undocumented) - actions?: TemplateAction_2[]; + actions?: TemplateAction_2[]; // (undocumented) additionalTemplateFilters?: | Record @@ -866,11 +844,11 @@ export type TemplateAction = // @public export class TemplateActionRegistry { // (undocumented) - get(actionId: string): TemplateAction_2; + get(actionId: string): TemplateAction_2; // (undocumented) - list(): TemplateAction_2[]; + list(): TemplateAction_2[]; // (undocumented) - register(action: TemplateAction_2): void; + register(action: TemplateAction_2): void; } // @public @deprecated (undocumented) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/TemplateActionRegistry.ts b/plugins/scaffolder-backend/src/scaffolder/actions/TemplateActionRegistry.ts index f00d7afad1..11d6234083 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/TemplateActionRegistry.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/TemplateActionRegistry.ts @@ -23,7 +23,7 @@ import { TemplateAction } from '@backstage/plugin-scaffolder-node'; export class TemplateActionRegistry { private readonly actions = new Map(); - register(action: TemplateAction) { + register(action: TemplateAction) { if (this.actions.has(action.id)) { throw new ConflictError( `Template action with ID '${action.id}' has already been registered`, @@ -33,7 +33,7 @@ export class TemplateActionRegistry { this.actions.set(action.id, action); } - get(actionId: string): TemplateAction { + get(actionId: string): TemplateAction { const action = this.actions.get(actionId); if (!action) { throw new NotFoundError( @@ -43,7 +43,7 @@ export class TemplateActionRegistry { return action; } - list(): TemplateAction[] { + list(): TemplateAction[] { return [...this.actions.values()]; } } diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.ts index 824ca4deb3..e21930ed78 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.ts @@ -16,7 +16,6 @@ import { CatalogApi } from '@backstage/catalog-client'; import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; -import { z } from 'zod'; import { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model'; import { examples } from './fetch.examples'; import { AuthService } from '@backstage/backend-plugin-api'; @@ -41,48 +40,54 @@ export function createFetchCatalogEntityAction(options: { examples, supportsDryRun: true, schema: { - input: z.object({ - entityRef: z - .string({ - description: 'Entity reference of the entity to get', - }) - .optional(), - entityRefs: z - .array(z.string(), { - description: 'Entity references of the entities to get', - }) - .optional(), - optional: z - .boolean({ - description: - 'Allow the entity or entities to optionally exist. Default: false', - }) - .optional(), - defaultKind: z.string({ description: 'The default kind' }).optional(), - defaultNamespace: z - .string({ description: 'The default namespace' }) - .optional(), - }), - output: z.object({ - entity: z - .any({ - description: - 'Object containing same values used in the Entity schema. Only when used with `entityRef` parameter.', - }) - .optional(), - entities: z - .array( - z.any({ + input: { + entityRef: z => + z + .string({ + description: 'Entity reference of the entity to get', + }) + .optional(), + entityRefs: z => + z + .array(z.string(), { + description: 'Entity references of the entities to get', + }) + .optional(), + optional: z => + z + .boolean({ description: - 'Array containing objects with same values used in the Entity schema. Only when used with `entityRefs` parameter.', - }), - ) - .optional(), - }), + 'Allow the entity or entities to optionally exist. Default: false', + }) + .optional(), + defaultKind: z => + z.string({ description: 'The default kind' }).optional(), + defaultNamespace: z => + z.string({ description: 'The default namespace' }).optional(), + }, + output: { + entity: z => + z + .any({ + description: + 'Object containing same values used in the Entity schema. Only when used with `entityRef` parameter.', + }) + .optional(), + entities: z => + z + .array( + z.any({ + description: + 'Array containing objects with same values used in the Entity schema. Only when used with `entityRefs` parameter.', + }), + ) + .optional(), + }, }, async handler(ctx) { const { entityRef, entityRefs, optional, defaultKind, defaultNamespace } = ctx.input; + if (!entityRef && !entityRefs) { if (optional) { return; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.test.ts index f037323c04..b0b16c6d12 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.test.ts @@ -144,4 +144,25 @@ describe('fs:delete', () => { expect(fileExists).toBe(false); }); }); + + it('should handle windows style file paths', async () => { + const files = ['unit-test-a.js', 'unit-test-b.js']; + + files.forEach(file => { + const filePath = resolvePath(workspacePath, file); + const fileExists = fs.existsSync(filePath); + expect(fileExists).toBe(true); + }); + + await action.handler({ + ...mockContext, + input: { files: files.map(file => `.\\${file}`) }, + }); + + files.forEach(file => { + const filePath = resolvePath(workspacePath, file); + const fileExists = fs.existsSync(filePath); + expect(fileExists).toBe(false); + }); + }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts index 5a06c7d2aa..b2f9cddea4 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts @@ -53,7 +53,11 @@ export const createFilesystemDeleteAction = () => { } for (const file of ctx.input.files) { - const safeFilepath = resolveSafeChildPath(ctx.workspacePath, file); + // globby cannot handle backslash file separators + const safeFilepath = resolveSafeChildPath( + ctx.workspacePath, + file, + ).replace(/\\/g, '/'); const resolvedPaths = await globby(safeFilepath, { cwd: ctx.workspacePath, absolute: true, diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts index 8614f99388..2d6ed2d57c 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts @@ -104,33 +104,37 @@ describe('NunjucksWorkflowRunner', () => { fakeActionHandler = jest.fn(); fakeTaskLog = jest.fn(); - actionRegistry.register({ - id: 'jest-mock-action', - description: 'Mock action for testing', - handler: fakeActionHandler, - }); - - actionRegistry.register({ - id: 'jest-validated-action', - description: 'Mock action for testing', - supportsDryRun: true, - handler: fakeActionHandler, - schema: { - input: { - type: 'object', - required: ['foo'], - properties: { - foo: { - type: 'number', - }, - }, - }, - }, - }); + actionRegistry.register( + createTemplateAction({ + id: 'jest-mock-action', + description: 'Mock action for testing', + handler: fakeActionHandler, + }), + ); actionRegistry.register( createTemplateAction({ - id: 'jest-zod-validated-action', + id: 'jest-validated-action', + description: 'Mock action for testing', + supportsDryRun: true, + handler: fakeActionHandler, + schema: { + input: { + type: 'object', + required: ['foo'], + properties: { + foo: { + type: 'number', + }, + }, + }, + }, + }), + ); + + actionRegistry.register( + createTemplateAction({ + id: 'jest-legacy-zod-validated-action', description: 'Mock action for testing', handler: fakeActionHandler, supportsDryRun: true, @@ -142,52 +146,80 @@ describe('NunjucksWorkflowRunner', () => { }) as TemplateAction, ); - actionRegistry.register({ - id: 'output-action', - description: 'Mock action for testing', - handler: async ctx => { - ctx.output('mock', 'backstage'); - ctx.output('shouldRun', true); - }, - }); + actionRegistry.register( + createTemplateAction({ + id: 'jest-zod-validated-action', + description: 'Mock ac', + supportsDryRun: true, + schema: { + input: { + foo: zod => zod.number(), + }, + output: { + test: zod => zod.string(), + }, + }, + handler: fakeActionHandler, + }), + ); - actionRegistry.register({ - id: 'checkpoints-action', - description: 'Mock action with checkpoints', - handler: async ctx => { - const key1 = await ctx.checkpoint({ - key: 'key1', - fn: async () => 'updated', - }); - const key2 = await ctx.checkpoint({ - key: 'key2', - fn: async () => 'updated', - }); - const key3 = await ctx.checkpoint({ - key: 'key3', - fn: async () => 'updated', - }); + actionRegistry.register( + createTemplateAction({ + id: 'output-action', + description: 'Mock action for testing', + handler: async ctx => { + ctx.output('mock', 'backstage'); + ctx.output('shouldRun', true); + }, + }), + ); - const key4 = await ctx.checkpoint({ - key: 'key4', - fn: () => {}, - }); + actionRegistry.register( + createTemplateAction({ + id: 'checkpoints-action', + description: 'Mock action with checkpoints', + schema: { + output: z.object({ + key1: z.string(), + key2: z.string(), + key3: z.string(), + key4: z.string(), + key5: z.string(), + }), + }, + handler: async ctx => { + const key1 = await ctx.checkpoint({ + key: 'key1', + fn: async () => 'updated', + }); + const key2 = await ctx.checkpoint({ + key: 'key2', + fn: async () => 'updated', + }); + const key3 = await ctx.checkpoint({ + key: 'key3', + fn: async () => 'updated', + }); - const key5 = await ctx.checkpoint({ - key: 'key5', - fn: async () => {}, - }); + const key4 = await ctx.checkpoint({ + key: 'key4', + fn: () => {}, + }); - ctx.output('key1', key1); - ctx.output('key2', key2); - ctx.output('key3', key3); + const key5 = await ctx.checkpoint({ + key: 'key5', + fn: async () => {}, + }); - // @ts-expect-error - this is void return - ctx.output('key4', key4); - // @ts-expect-error - this is void return - ctx.output('key5', key5); - }, - }); + ctx.output('key1', key1); + ctx.output('key2', key2); + ctx.output('key3', key3); + + ctx.output('key4', key4); + ctx.output('key5', key5); + }, + }), + ); mockedPermissionApi.authorizeConditional.mockResolvedValue([ { result: AuthorizeResult.ALLOW }, @@ -244,6 +276,25 @@ describe('NunjucksWorkflowRunner', () => { ); }); + it('should throw an error if the action has legacy zod schema and the input does not match', async () => { + const task = createMockTaskWithSpec({ + apiVersion: 'scaffolder.backstage.io/v1beta3', + parameters: {}, + output: {}, + steps: [ + { + id: 'test', + name: 'name', + action: 'jest-legacy-zod-validated-action', + }, + ], + }); + + await expect(runner.execute(task)).rejects.toThrow( + /Invalid input passed to action jest-legacy-zod-validated-action, instance requires property \"foo\"/, + ); + }); + it('should run the action when the zod validation passes', async () => { const task = createMockTaskWithSpec({ apiVersion: 'scaffolder.backstage.io/v1beta3', @@ -264,6 +315,26 @@ describe('NunjucksWorkflowRunner', () => { expect(fakeActionHandler).toHaveBeenCalledTimes(1); }); + it('should run the action when the zod validation passes with legacy zod', async () => { + const task = createMockTaskWithSpec({ + apiVersion: 'scaffolder.backstage.io/v1beta3', + parameters: {}, + output: {}, + steps: [ + { + id: 'test', + name: 'name', + action: 'jest-legacy-zod-validated-action', + input: { foo: 1 }, + }, + ], + }); + + await runner.execute(task); + + expect(fakeActionHandler).toHaveBeenCalledTimes(1); + }); + it('should run the action when the validation passes', async () => { const task = createMockTaskWithSpec({ apiVersion: 'scaffolder.backstage.io/v1beta3', diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/helper.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/helper.ts index 41425affc9..da47bfd563 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/helper.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/helper.ts @@ -38,13 +38,13 @@ export function generateExampleOutput(schema: Schema): unknown { return Object.fromEntries( Object.entries(schema.properties ?? {}).map(([key, value]) => [ key, - generateExampleOutput(value), + generateExampleOutput(value as Schema), ]), ); } else if (schema.type === 'array') { const [firstSchema] = [schema.items]?.flat(); if (firstSchema) { - return [generateExampleOutput(firstSchema)]; + return [generateExampleOutput(firstSchema as Schema)]; } return []; } else if (schema.type === 'string') { diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 35782ea441..1186e7d4f6 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -165,7 +165,7 @@ export interface RouterOptions { database: DatabaseService; catalogClient: CatalogApi; scheduler?: SchedulerService; - actions?: TemplateAction[]; + actions?: TemplateAction[]; /** * @deprecated taskWorkers is deprecated in favor of concurrentTasksLimit option with a single TaskWorker * @defaultValue 1 diff --git a/plugins/scaffolder-node-test-utils/CHANGELOG.md b/plugins/scaffolder-node-test-utils/CHANGELOG.md index f33c5fee2b..4f11504092 100644 --- a/plugins/scaffolder-node-test-utils/CHANGELOG.md +++ b/plugins/scaffolder-node-test-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder-node-test-utils +## 0.2.0-next.2 + +### Minor Changes + +- 36677bb: Use update `createTemplateAction` kinds + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.0-next.2 + - @backstage/backend-test-utils@1.3.1-next.2 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/types@1.2.1 + ## 0.1.20-next.1 ### Patch Changes diff --git a/plugins/scaffolder-node-test-utils/package.json b/plugins/scaffolder-node-test-utils/package.json index 3ed56e700b..9a3cd185ae 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.20-next.1", + "version": "0.2.0-next.2", "backstage": { "role": "node-library", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index cafea8aa43..a6ca28f04e 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,89 @@ # @backstage/plugin-scaffolder-node +## 0.8.0-next.2 + +### Minor Changes + +- 1a58846: **DEPRECATION**: We've deprecated the old way of defining actions using `createTemplateAction` with raw `JSONSchema` and type parameters, as well as using `zod` through an import. You can now use the new format to define `createTemplateActions` with `zod` provided by the framework. This change also removes support for `logStream` in the `context` as well as moving the `logger` to an instance of `LoggerService`. + + Before: + + ```ts + createTemplateAction<{ repoUrl: string }, { test: string }>({ + id: 'test', + schema: { + input: { + type: 'object', + required: ['repoUrl'], + properties: { + repoUrl: { type: 'string' }, + }, + }, + output: { + type: 'object', + required: ['test'], + properties: { + test: { type: 'string' }, + }, + }, + }, + handler: async ctx => { + ctx.logStream.write('blob'); + }, + }); + + // or + + createTemplateAction({ + id: 'test', + schema: { + input: z.object({ + repoUrl: z.string(), + }), + output: z.object({ + test: z.string(), + }), + }, + handler: async ctx => { + ctx.logStream.write('something'); + }, + }); + ``` + + After: + + ```ts + createTemplateAction({ + id: 'test', + schema: { + input: { + repoUrl: d => d.string(), + }, + output: { + test: d => d.string(), + }, + }, + handler: async ctx => { + // you can just use ctx.logger.log('...'), or if you really need a log stream you can do this: + const logStream = new PassThrough(); + logStream.on('data', chunk => { + ctx.logger.info(chunk.toString()); + }); + }, + }); + ``` + +### Patch Changes + +- 4f8b5b6: Allow signing git commits using configured private PGP key in scaffolder +- Updated dependencies + - @backstage/integration@1.16.2-next.0 + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-scaffolder-common@1.5.10-next.0 + ## 0.7.1-next.1 ### Patch Changes diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index d7f8fc042b..953d150243 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node", - "version": "0.7.1-next.1", + "version": "0.8.0-next.2", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "node-library", @@ -60,11 +60,12 @@ "@backstage/integration": "workspace:^", "@backstage/plugin-scaffolder-common": "workspace:^", "@backstage/types": "workspace:^", + "@isomorphic-git/pgp-plugin": "^0.0.7", "concat-stream": "^2.0.0", "fs-extra": "^11.2.0", "globby": "^11.0.0", "isomorphic-git": "^1.23.0", - "jsonschema": "^1.2.6", + "jsonschema": "^1.5.0", "p-limit": "^3.1.0", "tar": "^6.1.12", "winston": "^3.2.1", diff --git a/plugins/scaffolder-node/report-alpha.api.md b/plugins/scaffolder-node/report-alpha.api.md index 8ecc54f031..fa9e022bb4 100644 --- a/plugins/scaffolder-node/report-alpha.api.md +++ b/plugins/scaffolder-node/report-alpha.api.md @@ -122,7 +122,7 @@ export const restoreWorkspace: (opts: { // @alpha export interface ScaffolderActionsExtensionPoint { // (undocumented) - addActions(...actions: TemplateAction[]): void; + addActions(...actions: TemplateAction[]): void; } // @alpha diff --git a/plugins/scaffolder-node/report.api.md b/plugins/scaffolder-node/report.api.md index 333b47a6d1..6dabcbf346 100644 --- a/plugins/scaffolder-node/report.api.md +++ b/plugins/scaffolder-node/report.api.md @@ -4,6 +4,7 @@ ```ts import { BackstageCredentials } from '@backstage/backend-plugin-api'; +import { Expand } from '@backstage/types'; import { JsonObject } from '@backstage/types'; import { JsonValue } from '@backstage/types'; import { Logger } from 'winston'; @@ -24,34 +25,63 @@ import { z } from 'zod'; export type ActionContext< TActionInput extends JsonObject, TActionOutput extends JsonObject = JsonObject, -> = { - logger: Logger; - logStream: Writable; - secrets?: TaskSecrets; - workspacePath: string; - input: TActionInput; - checkpoint(opts: { - key: string; - fn: () => Promise | T; - }): Promise; - output( - name: keyof TActionOutput, - value: TActionOutput[keyof TActionOutput], - ): void; - createTemporaryDirectory(): Promise; - getInitiatorCredentials(): Promise; - task: { - id: string; - }; - templateInfo?: TemplateInfo; - isDryRun?: boolean; - user?: { - entity?: UserEntity; - ref?: string; - }; - signal?: AbortSignal; - each?: JsonObject; -}; + TSchemaType extends 'v1' | 'v2' = 'v1', +> = TSchemaType extends 'v2' + ? { + logger: LoggerService; + secrets?: TaskSecrets; + workspacePath: string; + input: TActionInput; + checkpoint(opts: { + key: string; + fn: () => Promise | T; + }): Promise; + output( + name: keyof TActionOutput, + value: TActionOutput[keyof TActionOutput], + ): void; + createTemporaryDirectory(): Promise; + getInitiatorCredentials(): Promise; + task: { + id: string; + }; + templateInfo?: TemplateInfo; + isDryRun?: boolean; + user?: { + entity?: UserEntity; + ref?: string; + }; + signal?: AbortSignal; + each?: JsonObject; + } + : { + logger: Logger; + logStream: Writable; + secrets?: TaskSecrets; + workspacePath: string; + input: TActionInput; + checkpoint(opts: { + key: string; + fn: () => Promise | T; + }): Promise; + output( + name: keyof TActionOutput, + value: TActionOutput[keyof TActionOutput], + ): void; + createTemporaryDirectory(): Promise; + getInitiatorCredentials(): Promise; + task: { + id: string; + }; + templateInfo?: TemplateInfo; + isDryRun?: boolean; + user?: { + entity?: UserEntity; + ref?: string; + }; + signal?: AbortSignal; + each?: JsonObject; + }; // @public (undocumented) export function addFiles(options: { @@ -106,6 +136,7 @@ export function commitAndPushBranch(options: { branch?: string; remoteRef?: string; remote?: string; + signingKey?: string; }): Promise<{ commitHash: string; }>; @@ -129,6 +160,7 @@ export function commitAndPushRepo(input: { }; branch?: string; remoteRef?: string; + signingKey?: string; }): Promise<{ commitHash: string; }>; @@ -148,34 +180,71 @@ export function createBranch(options: { logger?: Logger | undefined; }): Promise; -// @public -export const createTemplateAction: < +// @public @deprecated (undocumented) +export function createTemplateAction< TInputParams extends JsonObject = JsonObject, TOutputParams extends JsonObject = JsonObject, - TInputSchema extends Schema | z.ZodType = {}, - TOutputSchema extends Schema | z.ZodType = {}, - TActionInput extends JsonObject = TInputSchema extends z.ZodType< - any, - any, - infer IReturn - > - ? IReturn - : TInputParams, - TActionOutput extends JsonObject = TOutputSchema extends z.ZodType< - any, - any, - infer IReturn_1 - > - ? IReturn_1 - : TOutputParams, + TInputSchema extends JsonObject = JsonObject, + TOutputSchema extends JsonObject = JsonObject, + TActionInput extends JsonObject = TInputParams, + TActionOutput extends JsonObject = TOutputParams, >( action: TemplateActionOptions< TActionInput, TActionOutput, TInputSchema, - TOutputSchema + TOutputSchema, + 'v1' >, -) => TemplateAction; +): TemplateAction; + +// @public @deprecated (undocumented) +export function createTemplateAction< + TInputParams extends JsonObject = JsonObject, + TOutputParams extends JsonObject = JsonObject, + TInputSchema extends z.ZodType = z.ZodType, + TOutputSchema extends z.ZodType = z.ZodType, + TActionInput extends JsonObject = z.infer, + TActionOutput extends JsonObject = z.infer, +>( + action: TemplateActionOptions< + TActionInput, + TActionOutput, + TInputSchema, + TOutputSchema, + 'v1' + >, +): TemplateAction; + +// @public +export function createTemplateAction< + TInputSchema extends { + [key in string]: (zImpl: typeof z) => z.ZodType; + }, + TOutputSchema extends { + [key in string]: (zImpl: typeof z) => z.ZodType; + }, +>( + action: TemplateActionOptions< + { + [key in keyof TInputSchema]: z.infer>; + }, + { + [key in keyof TOutputSchema]: z.infer>; + }, + TInputSchema, + TOutputSchema, + 'v2' + >, +): TemplateAction< + FlattenOptionalProperties<{ + [key in keyof TInputSchema]: z.output>; + }>, + FlattenOptionalProperties<{ + [key in keyof TOutputSchema]: z.output>; + }>, + 'v2' +>; // @public export function deserializeDirectoryContents( @@ -242,6 +311,7 @@ export function initRepoAndPush(input: { name?: string; email?: string; }; + signingKey?: string; }): Promise<{ commitHash: string; }>; @@ -440,6 +510,7 @@ export type TaskStatus = export type TemplateAction< TActionInput extends JsonObject = JsonObject, TActionOutput extends JsonObject = JsonObject, + TSchemaType extends 'v1' | 'v2' = 'v1', > = { id: string; description?: string; @@ -452,15 +523,28 @@ export type TemplateAction< input?: Schema; output?: Schema; }; - handler: (ctx: ActionContext) => Promise; + handler: ( + ctx: ActionContext, + ) => Promise; }; // @public (undocumented) export type TemplateActionOptions< TActionInput extends JsonObject = {}, TActionOutput extends JsonObject = {}, - TInputSchema extends Schema | z.ZodType = {}, - TOutputSchema extends Schema | z.ZodType = {}, + TInputSchema extends + | JsonObject + | z.ZodType + | { + [key in string]: (zImpl: typeof z) => z.ZodType; + } = JsonObject, + TOutputSchema extends + | JsonObject + | z.ZodType + | { + [key in string]: (zImpl: typeof z) => z.ZodType; + } = JsonObject, + TSchemaType extends 'v1' | 'v2' = 'v1' | 'v2', > = { id: string; description?: string; @@ -470,7 +554,9 @@ export type TemplateActionOptions< input?: TInputSchema; output?: TOutputSchema; }; - handler: (ctx: ActionContext) => Promise; + handler: ( + ctx: ActionContext, + ) => Promise; }; // @public (undocumented) diff --git a/plugins/scaffolder-node/src/actions/createTemplateAction.test.ts b/plugins/scaffolder-node/src/actions/createTemplateAction.test.ts new file mode 100644 index 0000000000..43d5d2f660 --- /dev/null +++ b/plugins/scaffolder-node/src/actions/createTemplateAction.test.ts @@ -0,0 +1,131 @@ +/* + * 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 { createTemplateAction } from './createTemplateAction'; +import { z } from 'zod'; + +describe('createTemplateAction', () => { + it('should allow creating with jsonschema and use the old deprecated types', () => { + const action = createTemplateAction<{ repoUrl: string }, { test: string }>({ + id: 'test', + schema: { + input: { + type: 'object', + required: ['repoUrl'], + properties: { + repoUrl: { type: 'string' }, + }, + }, + output: { + type: 'object', + required: ['test'], + properties: { + test: { type: 'string' }, + }, + }, + }, + handler: async ctx => { + // @ts-expect-error - repoUrl is string + const a: number = ctx.input.repoUrl; + + const b: string = ctx.input.repoUrl; + expect(b).toBeDefined(); + + const stream = ctx.logStream; + expect(stream).toBeDefined(); + + ctx.output('test', 'value'); + + // @ts-expect-error - not valid output type + ctx.output('test', 4); + + // @ts-expect-error - not valid output name + ctx.output('test2', 'value'); + }, + }); + + expect(action).toBeDefined(); + }); + + it('should allow creating with zod and use the old deprecated types', () => { + const action = createTemplateAction({ + id: 'test', + schema: { + input: z.object({ + repoUrl: z.string(), + }), + output: z.object({ + test: z.string(), + }), + }, + handler: async ctx => { + // @ts-expect-error - repoUrl is string + const a: number = ctx.input.repoUrl; + + const b: string = ctx.input.repoUrl; + expect(b).toBeDefined(); + + const stream = ctx.logStream; + expect(stream).toBeDefined(); + + ctx.output('test', 'value'); + + // @ts-expect-error - not valid output type + ctx.output('test', 4); + + // @ts-expect-error - not valid output name + ctx.output('test2', 'value'); + }, + }); + + expect(action).toBeDefined(); + }); + + it('should allow creating with new first class zod support', () => { + const action = createTemplateAction({ + id: 'test', + schema: { + input: { + repoUrl: d => d.string(), + }, + output: { + test: d => d.string(), + }, + }, + handler: async ctx => { + // @ts-expect-error - repoUrl is string + const a: number = ctx.input.repoUrl; + + const b: string = ctx.input.repoUrl; + expect(b).toBeDefined(); + + // @ts-expect-error - logStream is not available + const stream = ctx.logStream; + + expect(stream).toBeDefined(); + + ctx.output('test', 'value'); + + // @ts-expect-error - not valid output type + ctx.output('test', 4); + + // @ts-expect-error - not valid output name + ctx.output('test2', 'value'); + }, + }); + + expect(action).toBeDefined(); + }); +}); diff --git a/plugins/scaffolder-node/src/actions/createTemplateAction.ts b/plugins/scaffolder-node/src/actions/createTemplateAction.ts index 4721df7de5..ce38f3b4e1 100644 --- a/plugins/scaffolder-node/src/actions/createTemplateAction.ts +++ b/plugins/scaffolder-node/src/actions/createTemplateAction.ts @@ -16,9 +16,8 @@ import { ActionContext, TemplateAction } from './types'; import { z } from 'zod'; -import { Schema } from 'jsonschema'; -import zodToJsonSchema from 'zod-to-json-schema'; -import { JsonObject } from '@backstage/types'; +import { Expand, JsonObject } from '@backstage/types'; +import { parseSchemas } from './util'; /** @public */ export type TemplateExample = { @@ -30,8 +29,15 @@ export type TemplateExample = { export type TemplateActionOptions< TActionInput extends JsonObject = {}, TActionOutput extends JsonObject = {}, - TInputSchema extends Schema | z.ZodType = {}, - TOutputSchema extends Schema | z.ZodType = {}, + TInputSchema extends + | JsonObject + | z.ZodType + | { [key in string]: (zImpl: typeof z) => z.ZodType } = JsonObject, + TOutputSchema extends + | JsonObject + | z.ZodType + | { [key in string]: (zImpl: typeof z) => z.ZodType } = JsonObject, + TSchemaType extends 'v1' | 'v2' = 'v1' | 'v2', > = { id: string; description?: string; @@ -41,25 +47,112 @@ export type TemplateActionOptions< input?: TInputSchema; output?: TOutputSchema; }; - handler: (ctx: ActionContext) => Promise; + handler: ( + ctx: ActionContext, + ) => Promise; }; +/** + * @ignore + */ +type FlattenOptionalProperties = Expand< + { + [K in keyof T as undefined extends T[K] ? never : K]: T[K]; + } & { + [K in keyof T as undefined extends T[K] ? K : never]?: T[K]; + } +>; + +/** + * @public + * @deprecated migrate to using the new built in zod schema definitions for schemas + */ +export function createTemplateAction< + TInputParams extends JsonObject = JsonObject, + TOutputParams extends JsonObject = JsonObject, + TInputSchema extends JsonObject = JsonObject, + TOutputSchema extends JsonObject = JsonObject, + TActionInput extends JsonObject = TInputParams, + TActionOutput extends JsonObject = TOutputParams, +>( + action: TemplateActionOptions< + TActionInput, + TActionOutput, + TInputSchema, + TOutputSchema, + 'v1' + >, +): TemplateAction; +/** + * @public + * @deprecated migrate to using the new built in zod schema definitions for schemas + */ +export function createTemplateAction< + TInputParams extends JsonObject = JsonObject, + TOutputParams extends JsonObject = JsonObject, + TInputSchema extends z.ZodType = z.ZodType, + TOutputSchema extends z.ZodType = z.ZodType, + TActionInput extends JsonObject = z.infer, + TActionOutput extends JsonObject = z.infer, +>( + action: TemplateActionOptions< + TActionInput, + TActionOutput, + TInputSchema, + TOutputSchema, + 'v1' + >, +): TemplateAction; /** * This function is used to create new template actions to get type safety. * Will convert zod schemas to json schemas for use throughout the system. * @public */ -export const createTemplateAction = < +export function createTemplateAction< + TInputSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType }, + TOutputSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType }, +>( + action: TemplateActionOptions< + { + [key in keyof TInputSchema]: z.infer>; + }, + { + [key in keyof TOutputSchema]: z.infer>; + }, + TInputSchema, + TOutputSchema, + 'v2' + >, +): TemplateAction< + FlattenOptionalProperties<{ + [key in keyof TInputSchema]: z.output>; + }>, + FlattenOptionalProperties<{ + [key in keyof TOutputSchema]: z.output>; + }>, + 'v2' +>; +export function createTemplateAction< TInputParams extends JsonObject = JsonObject, TOutputParams extends JsonObject = JsonObject, - TInputSchema extends Schema | z.ZodType = {}, - TOutputSchema extends Schema | z.ZodType = {}, + TInputSchema extends + | JsonObject + | z.ZodType + | { [key in string]: (zImpl: typeof z) => z.ZodType } = JsonObject, + TOutputSchema extends + | JsonObject + | z.ZodType + | { [key in string]: (zImpl: typeof z) => z.ZodType } = JsonObject, TActionInput extends JsonObject = TInputSchema extends z.ZodType< any, any, infer IReturn > ? IReturn + : TInputSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType } + ? Expand<{ + [key in keyof TInputSchema]: z.infer>; + }> : TInputParams, TActionOutput extends JsonObject = TOutputSchema extends z.ZodType< any, @@ -67,6 +160,10 @@ export const createTemplateAction = < infer IReturn > ? IReturn + : TOutputSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType } + ? Expand<{ + [key in keyof TOutputSchema]: z.infer>; + }> : TOutputParams, >( action: TemplateActionOptions< @@ -75,23 +172,23 @@ export const createTemplateAction = < TInputSchema, TOutputSchema >, -): TemplateAction => { - const inputSchema = - action.schema?.input && 'safeParseAsync' in action.schema.input - ? zodToJsonSchema(action.schema.input) - : action.schema?.input; - - const outputSchema = - action.schema?.output && 'safeParseAsync' in action.schema.output - ? zodToJsonSchema(action.schema.output) - : action.schema?.output; +): TemplateAction< + TActionInput, + TActionOutput, + TInputSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType } + ? 'v2' + : 'v1' +> { + const { inputSchema, outputSchema } = parseSchemas( + action as TemplateActionOptions, + ); return { ...action, schema: { ...action.schema, - input: inputSchema as TInputSchema, - output: outputSchema as TOutputSchema, + input: inputSchema, + output: outputSchema, }, }; -}; +} diff --git a/plugins/scaffolder-node/src/actions/gitHelpers.test.ts b/plugins/scaffolder-node/src/actions/gitHelpers.test.ts index 2e8eb9366f..d70ddf3bc0 100644 --- a/plugins/scaffolder-node/src/actions/gitHelpers.test.ts +++ b/plugins/scaffolder-node/src/actions/gitHelpers.test.ts @@ -16,12 +16,12 @@ import { Git } from '../scm'; import { - commitAndPushRepo, - initRepoAndPush, - commitAndPushBranch, addFiles, - createBranch, cloneRepo, + commitAndPushBranch, + commitAndPushRepo, + createBranch, + initRepoAndPush, } from './gitHelpers'; import { mockServices } from '@backstage/backend-test-utils'; import { loggerToWinstonLogger } from './loggerToWinstonLogger'; @@ -224,6 +224,32 @@ describe('commitAndPushRepo', () => { }); }); + it('creates commit with signing key', async () => { + await commitAndPushRepo({ + dir: '/test/repo/dir/', + auth: { + username: 'test-user', + password: 'test-password', + }, + logger: loggerToWinstonLogger(mockServices.logger.mock()), + commitMessage: 'commit message', + signingKey: 'test-signing-key', + }); + expect(mockedGit.commit).toHaveBeenCalledWith({ + dir: '/test/repo/dir/', + message: 'commit message', + author: { + name: 'Scaffolder', + email: 'scaffolder@backstage.io', + }, + committer: { + name: 'Scaffolder', + email: 'scaffolder@backstage.io', + }, + signingKey: 'test-signing-key', + }); + }); + it('pushes to the remote', () => { expect(mockedGit.push).toHaveBeenCalledWith({ dir: '/test/repo/dir/', diff --git a/plugins/scaffolder-node/src/actions/gitHelpers.ts b/plugins/scaffolder-node/src/actions/gitHelpers.ts index e24c0cf025..8922975274 100644 --- a/plugins/scaffolder-node/src/actions/gitHelpers.ts +++ b/plugins/scaffolder-node/src/actions/gitHelpers.ts @@ -31,6 +31,7 @@ export async function initRepoAndPush(input: { defaultBranch?: string; commitMessage?: string; gitAuthorInfo?: { name?: string; email?: string }; + signingKey?: string; }): Promise<{ commitHash: string }> { const { dir, @@ -40,6 +41,7 @@ export async function initRepoAndPush(input: { defaultBranch = 'master', commitMessage = 'Initial commit', gitAuthorInfo, + signingKey, } = input; const git = Git.fromAuth({ ...auth, @@ -64,6 +66,7 @@ export async function initRepoAndPush(input: { message: commitMessage, author: authorInfo, committer: authorInfo, + signingKey, }); await git.push({ @@ -89,6 +92,7 @@ export async function commitAndPushRepo(input: { gitAuthorInfo?: { name?: string; email?: string }; branch?: string; remoteRef?: string; + signingKey?: string; }): Promise<{ commitHash: string }> { const { dir, @@ -98,6 +102,7 @@ export async function commitAndPushRepo(input: { gitAuthorInfo, branch = 'master', remoteRef, + signingKey, } = input; const git = Git.fromAuth({ @@ -120,6 +125,7 @@ export async function commitAndPushRepo(input: { message: commitMessage, author: authorInfo, committer: authorInfo, + signingKey, }); await git.push({ @@ -213,6 +219,7 @@ export async function commitAndPushBranch(options: { branch?: string; remoteRef?: string; remote?: string; + signingKey?: string; }): Promise<{ commitHash: string }> { const { dir, @@ -223,6 +230,7 @@ export async function commitAndPushBranch(options: { branch = 'master', remoteRef, remote = 'origin', + signingKey, } = options; const git = Git.fromAuth({ ...auth, @@ -240,6 +248,7 @@ export async function commitAndPushBranch(options: { message: commitMessage, author: authorInfo, committer: authorInfo, + signingKey, }); await git.push({ diff --git a/plugins/scaffolder-node/src/actions/types.ts b/plugins/scaffolder-node/src/actions/types.ts index 171d3d82db..92a2c7464a 100644 --- a/plugins/scaffolder-node/src/actions/types.ts +++ b/plugins/scaffolder-node/src/actions/types.ts @@ -21,8 +21,10 @@ import { TaskSecrets } from '../tasks'; import { TemplateInfo } from '@backstage/plugin-scaffolder-common'; import { UserEntity } from '@backstage/catalog-model'; import { Schema } from 'jsonschema'; -import { BackstageCredentials } from '@backstage/backend-plugin-api'; - +import { + BackstageCredentials, + LoggerService, +} from '@backstage/backend-plugin-api'; /** * ActionContext is passed into scaffolder actions. * @public @@ -30,77 +32,143 @@ import { BackstageCredentials } from '@backstage/backend-plugin-api'; export type ActionContext< TActionInput extends JsonObject, TActionOutput extends JsonObject = JsonObject, -> = { - // TODO(blam): move this to LoggerService - logger: Logger; - /** @deprecated - use `ctx.logger` instead */ - logStream: Writable; - secrets?: TaskSecrets; - workspacePath: string; - input: TActionInput; - checkpoint(opts: { - key: string; - fn: () => Promise | T; - }): Promise; - output( - name: keyof TActionOutput, - value: TActionOutput[keyof TActionOutput], - ): void; + TSchemaType extends 'v1' | 'v2' = 'v1', +> = TSchemaType extends 'v2' + ? { + logger: LoggerService; + secrets?: TaskSecrets; + workspacePath: string; + input: TActionInput; + checkpoint(opts: { + key: string; + fn: () => Promise | T; + }): Promise; + output( + name: keyof TActionOutput, + value: TActionOutput[keyof TActionOutput], + ): void; + /** + * Creates a temporary directory for use by the action, which is then cleaned up automatically. + */ + createTemporaryDirectory(): Promise; - /** - * Creates a temporary directory for use by the action, which is then cleaned up automatically. - */ - createTemporaryDirectory(): Promise; + /** + * Get the credentials for the current request + */ + getInitiatorCredentials(): Promise; - /** - * Get the credentials for the current request - */ - getInitiatorCredentials(): Promise; + /** + * Task information + */ + task: { + id: string; + }; - /** - * Task information - */ - task: { - id: string; - }; + templateInfo?: TemplateInfo; - templateInfo?: TemplateInfo; + /** + * Whether this action invocation is a dry-run or not. + * This will only ever be true if the actions as marked as supporting dry-runs. + */ + isDryRun?: boolean; - /** - * Whether this action invocation is a dry-run or not. - * This will only ever be true if the actions as marked as supporting dry-runs. - */ - isDryRun?: boolean; + /** + * The user which triggered the action. + */ + user?: { + /** + * The decorated entity from the Catalog + */ + entity?: UserEntity; + /** + * An entity ref for the author of the task + */ + ref?: string; + }; - /** - * The user which triggered the action. - */ - user?: { - /** - * The decorated entity from the Catalog - */ - entity?: UserEntity; - /** - * An entity ref for the author of the task - */ - ref?: string; - }; + /** + * Implement the signal to make your custom step abortable https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal + */ + signal?: AbortSignal; - /** - * Implement the signal to make your custom step abortable https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal - */ - signal?: AbortSignal; + /** + * Optional value of each invocation + */ + each?: JsonObject; + } + : /** @deprecated **/ + { + // TODO(blam): move this to LoggerService + logger: Logger; + /** @deprecated - use `ctx.logger` instead */ + logStream: Writable; + secrets?: TaskSecrets; + workspacePath: string; + input: TActionInput; + checkpoint(opts: { + key: string; + fn: () => Promise | T; + }): Promise; + output( + name: keyof TActionOutput, + value: TActionOutput[keyof TActionOutput], + ): void; - /** - * Optional value of each invocation - */ - each?: JsonObject; -}; + /** + * Creates a temporary directory for use by the action, which is then cleaned up automatically. + */ + createTemporaryDirectory(): Promise; + + /** + * Get the credentials for the current request + */ + getInitiatorCredentials(): Promise; + + /** + * Task information + */ + task: { + id: string; + }; + + templateInfo?: TemplateInfo; + + /** + * Whether this action invocation is a dry-run or not. + * This will only ever be true if the actions as marked as supporting dry-runs. + */ + isDryRun?: boolean; + + /** + * The user which triggered the action. + */ + user?: { + /** + * The decorated entity from the Catalog + */ + entity?: UserEntity; + /** + * An entity ref for the author of the task + */ + ref?: string; + }; + + /** + * Implement the signal to make your custom step abortable https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal + */ + signal?: AbortSignal; + + /** + * Optional value of each invocation + */ + each?: JsonObject; + }; /** @public */ export type TemplateAction< TActionInput extends JsonObject = JsonObject, TActionOutput extends JsonObject = JsonObject, + TSchemaType extends 'v1' | 'v2' = 'v1', > = { id: string; description?: string; @@ -110,5 +178,7 @@ export type TemplateAction< input?: Schema; output?: Schema; }; - handler: (ctx: ActionContext) => Promise; + handler: ( + ctx: ActionContext, + ) => Promise; }; diff --git a/plugins/scaffolder-node/src/actions/util.ts b/plugins/scaffolder-node/src/actions/util.ts index 5e4ce60a1a..f1775bb318 100644 --- a/plugins/scaffolder-node/src/actions/util.ts +++ b/plugins/scaffolder-node/src/actions/util.ts @@ -18,6 +18,10 @@ import { InputError } from '@backstage/errors'; import { isChildPath } from '@backstage/backend-plugin-api'; import { join as joinPath, normalize as normalizePath } from 'path'; import { ScmIntegrationRegistry } from '@backstage/integration'; +import { TemplateActionOptions } from './createTemplateAction'; +import zodToJsonSchema from 'zod-to-json-schema'; +import { z } from 'zod'; +import { Schema } from 'jsonschema'; /** * @public @@ -124,3 +128,60 @@ function checkRequiredParams(repoUrl: URL, ...params: string[]) { } } } + +const isZodSchema = (schema: unknown): schema is z.ZodType => { + return typeof schema === 'object' && !!schema && 'safeParseAsync' in schema; +}; + +const isNativeZodSchema = ( + schema: unknown, +): schema is { [key in string]: (zImpl: typeof z) => z.ZodType } => { + return ( + typeof schema === 'object' && + !!schema && + Object.values(schema).every(v => typeof v === 'function') + ); +}; + +export const parseSchemas = ( + action: TemplateActionOptions, +): { inputSchema?: Schema; outputSchema?: Schema } => { + if (!action.schema) { + return { inputSchema: undefined, outputSchema: undefined }; + } + + if (isZodSchema(action.schema.input)) { + return { + inputSchema: zodToJsonSchema(action.schema.input) as Schema, + outputSchema: isZodSchema(action.schema.output) + ? (zodToJsonSchema(action.schema.output) as Schema) + : undefined, + }; + } + + if (isNativeZodSchema(action.schema.input)) { + const input = z.object( + Object.fromEntries( + Object.entries(action.schema.input).map(([k, v]) => [k, v(z)]), + ), + ); + + return { + inputSchema: zodToJsonSchema(input) as Schema, + outputSchema: isNativeZodSchema(action.schema.output) + ? (zodToJsonSchema( + z.object( + Object.fromEntries( + Object.entries(action.schema.output).map(([k, v]) => [k, v(z)]), + ), + ), + ) as Schema) + : undefined, + }; + } + + return { + inputSchema: action.schema.input, + outputSchema: action.schema.output, + }; +}; diff --git a/plugins/scaffolder-node/src/alpha/index.ts b/plugins/scaffolder-node/src/alpha/index.ts index 1b573d0650..d97aa54557 100644 --- a/plugins/scaffolder-node/src/alpha/index.ts +++ b/plugins/scaffolder-node/src/alpha/index.ts @@ -34,7 +34,7 @@ export * from './globals'; * @alpha */ export interface ScaffolderActionsExtensionPoint { - addActions(...actions: TemplateAction[]): void; + addActions(...actions: TemplateAction[]): void; } /** diff --git a/plugins/scaffolder-node/src/scm/git.test.ts b/plugins/scaffolder-node/src/scm/git.test.ts index 4a472a675a..dfbdd9cd16 100644 --- a/plugins/scaffolder-node/src/scm/git.test.ts +++ b/plugins/scaffolder-node/src/scm/git.test.ts @@ -16,6 +16,12 @@ jest.mock('isomorphic-git'); jest.mock('isomorphic-git/http/node'); jest.mock('fs-extra'); +jest.mock('@isomorphic-git/pgp-plugin', () => ({ + ...jest.requireActual('@isomorphic-git/pgp-plugin'), + pgp: { + sign: jest.fn().mockResolvedValue({ signature: 'sign' }), + }, +})); import * as isomorphic from 'isomorphic-git'; import { Git } from './git'; @@ -140,8 +146,9 @@ describe('Git', () => { name: 'comitter', email: 'test@backstage.io', }; + const signingKey = 'test-signing-key'; - await git.commit({ dir, message, author, committer }); + await git.commit({ dir, message, author, committer, signingKey }); expect(isomorphic.commit).toHaveBeenCalledWith({ fs, @@ -149,6 +156,8 @@ describe('Git', () => { message, author, committer, + signingKey, + onSign: expect.any(Function), }); }); }); diff --git a/plugins/scaffolder-node/src/scm/git.ts b/plugins/scaffolder-node/src/scm/git.ts index 760be73114..42926fd901 100644 --- a/plugins/scaffolder-node/src/scm/git.ts +++ b/plugins/scaffolder-node/src/scm/git.ts @@ -15,14 +15,16 @@ */ import git, { - ProgressCallback, - MergeResult, - ReadCommitResult, AuthCallback, + MergeResult, + ProgressCallback, + ReadCommitResult, } from 'isomorphic-git'; import http from 'isomorphic-git/http/node'; import fs from 'fs-extra'; import { LoggerService } from '@backstage/backend-plugin-api'; +// @ts-ignore +import { pgp } from '@isomorphic-git/pgp-plugin'; function isAuthCallbackOptions( options: StaticAuthOptions | AuthCallbackOptions, @@ -137,12 +139,21 @@ export class Git { message: string; author: { name: string; email: string }; committer: { name: string; email: string }; + signingKey?: string; }): Promise { - const { dir, message, author, committer } = options; + const { dir, message, author, committer, signingKey } = options; this.config.logger?.info( `Committing file to repo {dir=${dir},message=${message}}`, ); - return git.commit({ fs, dir, message, author, committer }); + return git.commit({ + fs, + dir, + message, + author, + committer, + signingKey, + onSign: signingKey ? pgp.sign : undefined, + }); } /** https://isomorphic-git.org/docs/en/clone */ @@ -241,8 +252,9 @@ export class Git { ours?: string; author: { name: string; email: string }; committer: { name: string; email: string }; + signingKey?: string; }): Promise { - const { dir, theirs, ours, author, committer } = options; + const { dir, theirs, ours, author, committer, signingKey } = options; this.config.logger?.info( `Merging branch '${theirs}' into '${ours}' for repository {dir=${dir}}`, ); @@ -255,6 +267,8 @@ export class Git { theirs, author, committer, + signingKey, + onSign: signingKey ? pgp.sign : undefined, }); } diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index f58e3b88ac..53c8d0858f 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-scaffolder-react +## 1.14.6-next.2 + +### Patch Changes + +- 48aab13: Add i18n support for scaffolder-react plugin +- 3db64ba: Disable the submit button on creating +- 34ea3f5: Updated dependency `flatted` to `3.3.3`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-plugin-api@1.10.4 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-permission-react@0.4.31 + - @backstage/plugin-scaffolder-common@1.5.10-next.0 + ## 1.14.6-next.1 ### Patch Changes diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index a500ac1b49..6fc4fa34b9 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-react", - "version": "1.14.6-next.1", + "version": "1.14.6-next.2", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", "backstage": { "role": "web-library", @@ -81,7 +81,7 @@ "ajv": "^8.0.1", "ajv-errors": "^3.0.0", "classnames": "^2.2.6", - "flatted": "3.3.2", + "flatted": "3.3.3", "humanize-duration": "^3.25.1", "immer": "^9.0.6", "json-schema": "^0.4.0", diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx index 5f3dfdfa1d..c1d24ac6c6 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx @@ -15,17 +15,16 @@ */ import { renderInTestApp } from '@backstage/test-utils'; import { JsonValue } from '@backstage/types'; +import type { RJSFValidationError } from '@rjsf/utils'; import { act, fireEvent, waitFor } from '@testing-library/react'; import React, { useEffect } from 'react'; +import { FieldExtensionComponentProps } from '../../../extensions'; import { LayoutTemplate } from '../../../layouts'; import { SecretsContextProvider } from '../../../secrets'; import { TemplateParameterSchema } from '../../../types'; import { Stepper } from './Stepper'; -import type { RJSFValidationError } from '@rjsf/utils'; -import { FieldExtensionComponentProps } from '../../../extensions'; - describe('Stepper', () => { it('should render the step titles for each step of the manifest', async () => { const manifest: TemplateParameterSchema = { @@ -762,7 +761,10 @@ describe('Stepper', () => { ], }; - const onCreate = jest.fn(); + // `onCreate` must be async to mock the submit button disabled behavior + const onCreate = jest.fn( + () => new Promise(resolve => setTimeout(resolve, 0)), + ); const { getByRole } = await renderInTestApp( @@ -783,10 +785,18 @@ describe('Stepper', () => { fireEvent.click(getByRole('button', { name: 'Review' })); }); + fireEvent.click(getByRole('button', { name: 'Create' })); + + await waitFor(() => + expect(getByRole('button', { name: 'Create' })).toBeDisabled(), + ); + await act(async () => { fireEvent.click(getByRole('button', { name: 'Create' })); }); + expect(onCreate).toHaveBeenCalledTimes(1); + expect(onCreate).toHaveBeenCalledWith( expect.objectContaining({ thing: { repoOrg: 'backstage' }, diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx index 9ca37e284a..7508985a5c 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx @@ -115,7 +115,7 @@ export type StepperProps = { */ export const Stepper = (stepperProps: StepperProps) => { const { t } = useTranslationRef(scaffolderReactTranslationRef); - const { layouts = [], components = {}, ...props } = stepperProps; + const { layouts = [], components = {}, onCreate, ...props } = stepperProps; const { ReviewStateComponent = ReviewState, ReviewStepComponent, @@ -220,10 +220,17 @@ export const Stepper = (stepperProps: StepperProps) => { const mergedUiSchema = merge({}, propUiSchema, currentStep?.uiSchema); - const handleCreate = useCallback(() => { - props.onCreate(stepsState); + const [isCreating, setIsCreating] = useState(false); + + const handleCreate = useCallback(async () => { + setIsCreating(true); analytics.captureEvent('click', `${createLabel}`); - }, [props, stepsState, analytics, createLabel]); + try { + await onCreate(stepsState); + } finally { + setIsCreating(false); + } + }, [analytics, createLabel, onCreate, stepsState]); return ( <> @@ -318,6 +325,7 @@ export const Stepper = (stepperProps: StepperProps) => { {backLabel}